{UText4}
                                    {TEXT SELECTION TYPES AND COMMANDS}


{changed 04/27/84 1307  Change to TTypingCmd.Perform}
{changed 04/25/84 1406  Got rid of IF numParas = 1 in Adjust proc in InsertText}
{changed 04/25/84 1250  Changed TStyleCmd.FilterAndDo back to filtering TParaImage for Compugraphic}
{changed 04/19/84 1308  Set up fields for partial line erasing in KeyPause}
{changed 04/17/84 1720  In TTextSelection.CutCopy, changed "IF firstLP >= 0" to "IF firstLP > 0"}
{changed 04/16/84 1414  Set viewLRect origined at (0,0) in TTextSelection.CutCopy}
{changed 04/16/84 1322  Set textImage.minHeight to 0 in TOnePara and TMultiPara.CopySelf}
{changed 04/16/84 1024  Call recomputeImages in TOnePara and TMultiPara.CopySelf to set clipView size}
{changed 04/13/84 1736  First parameter decremented in calls to TParagraph.StyleAt;
                        No longer set unHighlightBefore[doPhase] to FALSE in typingCmd;
                        Set TInsertionPoint.justReturned to FALSE in all Key methods (except KeyReturn);
                        Call StyleFromContext in TStyleCmd.Perform}
{changed 04/13/84 1531  TStyleCmd.FilterAndDo now filters a TEditPara rather than a TParaImage;
                        Call StyleFromContext through a filter in TTextSelection.CREATE}
{changed 04/13/84 1102  TInsertion.MousePress: Allow selecting of word when double click on last half
                            of last character in word}
{changed 04/13/84 0209  Changed all calls to TEditPara.CREATE to TTextImage.NewEditPara}
{changed 04/09/84 1530  Set highlightAfter[doPhase] FALSE in TTypingCmd.CREATE}
{changed 04/09/84 1337  Set deferUpdate TRUE in TTypingCmd.Perform (do phase)}
{changed 04/09/84 1202  TInsertion.MousePress: Made sure all insertion points had their isPara flags
                            set when triple click on an empty paragraph;
                        TInsertionPoint.MouseMove: Don't unHighlight if isPara was TRUE}



{$S SgTxtHot}


METHODS OF TTextSelection;

{$S SgTxtHot}
    FUNCTION TTextSelection.CREATE(object: TObject; heap: THeap; itsView: TView;
                                            itsTextImage: TTextImage; itsAnchorLPt: LPoint;
                                            beginPara: TEditPara; beginIndex: LONGINT; beginLP: INTEGER;
                                            endPara: TEditPara; endIndex: LONGINT; endLP: INTEGER
                                            ): TTextSelection;

        {Need to filter paragraph before asking about its type styles}
        PROCEDURE FindFilteredStyle(obj: TObject);
            BEGIN
            SELF.StyleFromContext;
            END;

    VAR range:  TTextRange;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        IF object = NIL THEN
            object := NewObject(heap, THISCLASS);
        SELF := TTextSelection(TSelection.CREATE(object, heap, itsView, somethingKind, itsAnchorLPt));

        range := TTextRange.CREATE(NIL, heap, beginPara, beginIndex, beginLP, endPara, endIndex, endLP);
        WITH SELF DO
            BEGIN
            textImage := itsTextImage;
            textRange := range;
            isWordSelection := FALSE;
            isParaSelection := FALSE;
            amTyping := FALSE;
            viewTick := -1; { force recalculation of selection range }
            END;
        SELF.textImage.FilterAndDo(TParaImage(beginPara.images.First), FindFilteredStyle);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TTextSelection.Free;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.textRange.Free;
        SUPERSELF.Free;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    FUNCTION TTextSelection.Clone(heap: THeap): TObject;
    VAR textSel:    TTextSelection;
        range:	    TTextRange;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        range := TTextRange(SELF.textRange.Clone(heap));
        textSel := TTextSelection(SUPERSELF.Clone(heap));
        textSel.textRange := range;
        Clone := textSel;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
{$IFC fTextTrace}
    PROCEDURE TTextSelection.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        SUPERSELF.Fields(Field);
        Field('textImage: TTextImage');
        Field('textRange: TTextRange');
        Field('isWordSelection: BOOLEAN');
        Field('isParaSelection: BOOLEAN');
        Field('viewTick: INTEGER');
        Field('amTyping: BOOLEAN');
        Field('onFaces: INTEGER');
        Field('font: INTEGER');
        Field('');
    END;
{$ENDC}


{$S SgTxtHot}
    FUNCTION TTextSelection.BecomeInsertionPoint: TInsertionPoint;
    VAR insertionPt:    TInsertionPoint;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        insertionPt := TInsertionPoint(SELF.FreedAndReplacedBy(
                                TInsertionPoint.CREATE(NIL, SELF.Heap,
                                                        SELF.view, SELF.textImage, SELF.anchorLPt,
                                                            SELF.textRange.firstPara,
                                                            SELF.textRange.firstIndex,
                                                            SELF.textRange.firstLP)));
        SELF.textImage.text.ChangeSelInOtherPanels(insertionPt);
        BecomeInsertionPoint := insertionPt;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    FUNCTION TTextSelection.CanDoCommand(cmdNumber: TCmdNumber; VAR checkIt: BOOLEAN): BOOLEAN;
    VAR typeStyle:  TTypeStyle;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        CASE cmdNumber OF
            uModern, uClassic:
                BEGIN
                CanDoCommand := TRUE;
                typeStyle := SELF.currTypeStyle;
                checkIt := (cmdNumber - uModern + 1) = typeStyle.font.fontFamily;
                END;

            u20Pitch, u15Pitch, u12Pitch, u10Pitch, u12Point, u14Point, u18Point, u24Point:
                BEGIN
                CanDoCommand := TRUE;
                typeStyle := SELF.currTypeStyle;
                checkIt := (cmdNumber - u20Pitch + 1) = typeStyle.font.fontSize;
                END;

            {$IFC LibraryVersion <= 20}
            uFnt0, uFnt1, uFnt2, uFnt3, uFnt4, uFnt5, uFnt6, uFnt7, uFnt8, uFnt9, uFnt10, uFnt11:
                BEGIN
                CanDoCommand := TRUE;
                WITH SELF.currTypeStyle.font DO
                    CASE cmdNumber OF
                        uFnt0:	 checkIt := fontFamily = famSystem;
                        uFnt1:	 checkIt := (fontFamily = famModern)  AND (fontSize = size15Pitch);
                        uFnt2:	 checkIt := (fontFamily = famModern)  AND (fontSize = size12Pitch);
                        uFnt3:	 checkIt := (fontFamily = famClassic) AND (fontSize = size12Pitch);
                        uFnt4:	 checkIt := (fontFamily = famModern)  AND (fontSize = size10Pitch);
                        uFnt5:	 checkIt := (fontFamily = famModern)  AND (fontSize = size14Point);
                        uFnt6:	 checkIt := (fontFamily = famModern)  AND (fontSize = size12Point);
                        uFnt7:	 checkIt := (fontFamily = famClassic) AND (fontSize = size12Point);
                        uFnt8:	 checkIt := (fontFamily = famModern)  AND (fontSize = size18Point);
                        uFnt9:	 checkIt := (fontFamily = famClassic) AND (fontSize = size18Point);
                        uFnt10:	 checkIt := (fontFamily = famModern)  AND (fontSize = size24Point);
                        uFnt11:	 checkIt := (fontFamily = famClassic) AND (fontSize = size24Point);
                        END;
                END;
            {$ENDC}

            uPlain, uBold, uItalic, uUnderline, uShadow, uOutline:
                BEGIN
                CanDoCommand := TRUE;
                typeStyle := SELF.currTypeStyle;
                WITH typeStyle DO
                    CASE cmdNumber OF
                        uPlain:	    checkIt := onFaces=[];
                        uBold:	    checkIt := (bold	   in onFaces);
                        uItalic:    checkIt := (italic	   in onFaces);
                        uUnderline: checkIt := (underlined in onFaces);
                        uShadow:    checkIt := (shadow	   in onFaces);
                        uOutline:   checkIt := (outline	   in onFaces);
                        END;
                END;

            uCut, uCopy, uClear:
                CanDoCommand := TRUE;

            uPaste:
                BEGIN
                clipboard.Inspect;
                IF (clipboard.hasView) OR (clipBoard.hasUniversalText) THEN
                    CanDoCommand := TRUE;
                END;

            OTHERWISE
                CanDoCommand := SUPERSELF.CanDoCommand(cmdNumber, checkIt);
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TTextSelection.ChangeStyle(cmdNumber: TCmdNumber);
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        Write(chr(7),'Change typestyle not implemented for this selection type. ');
        WriteLn ('(cmdNumber= ',cmdNumber:1,')');
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TTextSelection.ChangeText(PROCEDURE TextEdit; PROCEDURE Adjust);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.MarkChanged;
        TextEdit;
        Adjust;
        IF NOT deferUpdate THEN
            BEGIN
            SELF.textImage.text.RecomputeImages;
            (*SELF.textImage.text.Invalidate;*)
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TTextSelection.CopySelf(heap: THeap; view: TView): TMultiParaSelection;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        CopySelf := NIL;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TTextSelection.CutCopy(clipSelection: TSelection; deleteOriginal: BOOLEAN);
    VAR clipHeap:	    THeap;
        clipPanel:	    TPanel;
        clipView:	    TTextView;
        viewLRect:	    LRect;
        text:		    TText;
        insPt:		    TInsertionPoint;
        clipTextSelection:  TMultiParaSelection;
        selSize:	    INTEGER;
        checkLast:	    BOOLEAN;

    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        clipHeap := clipSelection.heap;
        clipPanel := clipSelection.panel;
        viewLRect := SELF.textImage.extentLRect;
        SetLRect(viewLRect, 0, 0, viewLRect.right - viewLRect.left + 2 * cHorizMargin,
                                        viewLRect.bottom - viewLRect.top + 2 * cVertMargin);
        clipView := TTextView.CREATE(NIL, clipHeap, clipPanel, viewLRect);
        clipTextSelection := TMultiParaSelection(clipSelection.FreedAndReplacedBy(
                                                        SELF.CopySelf(clipHeap, clipView)));
        clipView.valid := TRUE;
        clipTextSelection.isParaSelection := SELF.isParaSelection;
        clipTextSelection.isWordSelection := SELF.isWordSelection;
        {Intelligence!!	 Even if word flag not set, let's see if we qualify as a word selection anyway}
        IF NOT SELF.isWordSelection THEN
            WITH SELF.textRange DO
                {$H-}
                clipTextSelection.isWordSelection := firstPara.Qualifies(firstLP) AND
                                                     lastPara.Qualifies(lastLP-1) AND
                                                     NOT firstPara.Qualifies(firstLP-1) AND
                                                     NOT lastPara.Qualifies(lastLP);
                {$H+}
        clipView.textImage := clipTextSelection.textImage;
        IF deleteOriginal THEN
            BEGIN
            {Intelligent Cut: Delete extra space}
            IF clipTextSelection.isWordSelection THEN
                WITH SELF.textRange DO
                    BEGIN
                    {$H-}
                    checkLast := TRUE;
                    IF firstLP > 0 THEN
                        IF firstPara.At(firstLp) = ' ' THEN
                            BEGIN
                            firstLP := firstLP - 1;
                            checkLast := FALSE;
                            END;
                    IF checkLast AND (lastLP < lastPara.size) THEN
                        IF lastPara.At(lastLP + 1) = ' ' THEN
                            lastLP := lastLP + 1;
                    {$H+}
                    END;
            SELF.DeleteAndFree;
            insPt := SELF.BecomeInsertionPoint;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TTextSelection.DeleteAndFree;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TTextSelection.DeleteButSave: TText;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        DeleteButSave := NIL;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TTextSelection.DoChangeStyle(cmdNumber: TCmdNumber; paragraph: TParagraph;
                                        firstLP: INTEGER; lastLP: INTEGER; VAR newStyle: TTypeStyle);
    VAR onFaces:    {$IFC LibraryVersion <= 20}TSeteface{$ELSEC}Style{$ENDC};
        faceChange: BOOLEAN;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        {$IFC fTextTrace}
        IF fTextTrace THEN
            BEGIN
            WriteLn('_+_ Entering TTextSelection.DoChangeStyle: firstLP = ', firstLP:1,
                                                                '  lastLP = ', lastLP:1);
            END;
        {$ENDC}
        faceChange := TRUE;
        CASE cmdNumber OF
            uPlain:
                onFaces := [];
            uBold:
                onFaces := [bold];
            uItalic:
                onFaces := [italic];
            uUnderline:
                onFaces := [underlined];
            uShadow:
                onFaces := [shadow];
            uOutline:
                onFaces := [outline];

            uModern, uClassic:
                BEGIN
                faceChange := FALSE;
                paragraph.ChgFontFamily(firstLP, lastLP, cmdNumber - uModern + 1, newStyle);
                END;

            {$IFC LibraryVersion <= 20}
            uFnt0, uFnt1, uFnt2, uFnt3, uFnt4, uFnt5, uFnt6, uFnt7, uFnt8, uFnt9, uFnt10, uFnt11:
                BEGIN
                faceChange := FALSE;
                CASE cmdNumber OF
                    uFnt0:   paragraph.ChgFontFamily(firstLP, lastLP, famSystem, newStyle);
                    uFnt1:
                        BEGIN
                        paragraph.ChgFontFamily(firstLP, lastLP, famModern, newStyle);
                        paragraph.ChgFontSize(firstLP, lastLP, size15Pitch, newStyle);
                        END;
                    uFnt2:
                        BEGIN
                        paragraph.ChgFontFamily(firstLP, lastLP, famModern, newStyle);
                        paragraph.ChgFontSize(firstLP, lastLP, size12Pitch, newStyle);
                        END;
                   uFnt3:
                        BEGIN
                        paragraph.ChgFontFamily(firstLP, lastLP, famClassic, newStyle);
                        paragraph.ChgFontSize(firstLP, lastLP, size12Pitch, newStyle);
                        END;
                   uFnt4:
                        BEGIN
                        paragraph.ChgFontFamily(firstLP, lastLP, famModern, newStyle);
                        paragraph.ChgFontSize(firstLP, lastLP, size10Pitch, newStyle);
                        END;
                    uFnt5:
                        BEGIN
                        paragraph.ChgFontFamily(firstLP, lastLP, famModern, newStyle);
                        paragraph.ChgFontSize(firstLP, lastLP, size14Point, newStyle);
                        END;
                    uFnt6:
                        BEGIN
                        paragraph.ChgFontFamily(firstLP, lastLP, famModern, newStyle);
                        paragraph.ChgFontSize(firstLP, lastLP, size12Point, newStyle);
                        END;
                    uFnt7:
                        BEGIN
                        paragraph.ChgFontFamily(firstLP, lastLP, famClassic, newStyle);
                        paragraph.ChgFontSize(firstLP, lastLP, size12Point, newStyle);
                        END;
                    uFnt8:
                        BEGIN
                        paragraph.ChgFontFamily(firstLP, lastLP, famModern, newStyle);
                        paragraph.ChgFontSize(firstLP, lastLP, size18Point, newStyle);
                        END;
                    uFnt9:
                        BEGIN
                        paragraph.ChgFontFamily(firstLP, lastLP, famClassic, newStyle);
                        paragraph.ChgFontSize(firstLP, lastLP, size18Point, newStyle);
                        END;
                    uFnt10:
                        BEGIN
                        paragraph.ChgFontFamily(firstLP, lastLP, famModern, newStyle);
                        paragraph.ChgFontSize(firstLP, lastLP, size24Point, newStyle);
                        END;
                    uFnt11:
                        BEGIN
                        paragraph.ChgFontFamily(firstLP, lastLP, famClassic, newStyle);
                        paragraph.ChgFontSize(firstLP, lastLP, size24Point, newStyle);
                        END;
                    END;
                END;
            {$ENDC}

            OTHERWISE
                BEGIN
                faceChange := FALSE;
                paragraph.ChgFontSize(firstLP, lastLP, cmdNumber - u20Pitch + 1, newStyle);
                END;
            END;

        IF faceChange THEN
            paragraph.ChgFace(firstLP, lastLP, onFaces, newStyle);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TTextSelection.GetHysteresis(VAR hysterPt: Point);
    BEGIN
        {$IFC fTrace}BP(6);{$ENDC}
        hysterPt := zeroPt;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TTextSelection.Highlight(highTransit: THighTransit);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        {Note that this is called from TPanel.Highlight which does an OnAllPadsDo, thus we do not
            call TText.HiliteParagraphs, but rather we call TTextImage.HiliteText }
        IF SELF.textImage.imageList.size > 0 THEN
            WITH SELF.textRange DO
                {$H-}
                SELF.textImage.headTxtImg.HiliteText(highTransit, firstIndex, firstLP, lastIndex, lastLP,
                                                SELF.isParaSelection);
                {$H+}
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TTextSelection.Invalidate;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.textImage.text.Invalidate;
        {$IFC fTrace}EP;{$ENDC}
    END;


(* This is now done automatically by the ToolKit
{$S SgTxtHot}
    PROCEDURE TTextSelection.KeyClear;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.window.PerformCommand(TClearTextCmd.CREATE(NIL, SELF.Heap, uClear,
                                                SELF.textImage, SELF.textImage.text));
        {$IFC fTrace}EP;{$ENDC}
    END;
*)


{$S SgTxtHot}
    PROCEDURE TTextSelection.KeyBack(fWord: BOOLEAN);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        {This will create the typing command, which deletes any selected text}
        SELF.KeyText;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TTextSelection.KeyChar(ch: CHAR);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.KeyText;
        SELF.KeyChar(ch); {!!! Assumes SELF was converted to insertion point}
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    PROCEDURE TTextSelection.KeyReturn;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.KeyText;
        SELF.KeyReturn; {!!! Assumes SELF was converted to insertion point}
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TTextSelection.KeyText;
    BEGIN
        {$IFC fTrace}BP(7);{$ENDC}
        IF NOT SELF.amTyping THEN
            SELF.window.PerformCommand(TTypingCmd.CREATE(NIL, SELF.Heap,
                                                SELF.textImage, SELF.textImage.text));
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TTextSelection.MarkChanged;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SUPERSELF.MarkChanged;
        SELF.textImage.text.MarkChanged(SELF.textRange);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TTextSelection.MousePress(mouseLPt: LPoint);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF clickState.fShift THEN
            SELF.MouseMove(mouseLPt)
        ELSE
            SELF.textImage.MousePress(mouseLPt);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TTextSelection.MouseRelease;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        ObscureCursor;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TTextSelection.NewCommand(cmdNumber: TCmdNumber): TCommand;
    VAR heap:	        THeap;
        textImage:      TTextImage;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        NewCommand := NIL;
        heap := SELF.heap;
        textImage := SELF.textImage;
        CASE cmdNumber OF
            u20Pitch, u15Pitch, u12Pitch, u10Pitch, u12Point, u14Point, u18Point, u24Point,
                    uModern, uClassic,
                    uPlain, uBold, uItalic, uUnderline, uShadow, uOutline:
                NewCommand := SELF.NewStyleCmd(heap, cmdNumber, textImage);

            {$IFC LibraryVersion <= 20}
            uFnt0, uFnt1, uFnt2, uFnt3, uFnt4, uFnt5, uFnt6, uFnt7, uFnt8, uFnt9, uFnt10, uFnt11:
                NewCommand := SELF.NewStyleCmd(heap, cmdNumber, textImage);
            {$ENDC}

            uCut, uCopy:
                NewCommand := SELF.NewCutCopyCmd(heap, cmdNumber, textImage);

            uClear:
                NewCommand := TClearTextCmd.CREATE(NIL, SELF.Heap, uClear, textImage, textImage.text);

            uPaste:
                NewCommand := TTextPaste.CREATE(NIL, heap, textImage, textImage.text);

            {cFormatChanges:}

            OTHERWISE
                NewCommand := SUPERSELF.NewCommand(cmdNumber);
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TTextSelection.NewCutCopyCmd(heap: THeap; cmdNumber: TCmdNumber;
                                    textImage: TTextImage): TCommand;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        NewCutCopyCmd := TTextCutCopy.CREATE(NIL, heap, cmdNumber, textImage,
                                                    cmdNumber=uCut, textImage.text);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TTextSelection.NewStyleCmd(heap: THeap; cmdNumber: TCmdNumber;
                                    textImage: TTextImage): TCommand;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        WITH SELF.textRange DO
            {$H-}
            NewStyleCmd := TStyleCmd.CREATE(NIL, heap, cmdNumber, textImage, firstIndex, lastIndex,
                                                firstLP, lastLP, SELF);
            {$H+}
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TTextSelection.ReplicateForOtherPanel(itsTextImage: TTextImage): TTextSelection;
    VAR sel:    TTextSelection;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        WITH SELF.textRange DO
            {$H-}
            sel := itsTextImage.NewTextSelection(firstPara, firstIndex, firstLP,
                                                 lastPara, lastIndex, lastLP);
            {$H+}
        sel.isParaSelection := SELF.isParaSelection;
        sel.isWordSelection := SELF.isWordSelection;
        ReplicateForOtherPanel := sel;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TTextSelection.StyleFromContext;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtIni}
END; {METHODS OF TTextSelection}


METHODS OF TInsertionPoint;

{$S SgTxtHot}
    FUNCTION TInsertionPoint.CREATE(object: TObject; heap: THeap; itsView: TView; itsTextImage: TTextImage;
                                       itsAnchorLPt: LPoint; itsParagraph: TEditPara; itsIndex: LONGINT;
                                       itsLP: INTEGER): TInsertionPoint;
    VAR s:	    TListScanner;
        paragraph:  TEditPara;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF object = NIL THEN
            object := NewObject(heap, THISCLASS);

        IF itsIndex <= 0 THEN
            BEGIN
            s := itsTextImage.text.paragraphs.Scanner;
            WHILE s.Scan(paragraph) DO
                IF paragraph = itsParagraph THEN
                    BEGIN
                    itsIndex := s.position;
                    s.Done;
                    END;
            END;

        SELF := TInsertionPoint(TTextSelection.CREATE(object, heap, itsView, itsTextImage, itsAnchorLPt,
                                            itsParagraph, itsIndex, itsLP, itsParagraph, itsIndex, itsLP));

        itsParagraph.bsCount := 0;
        WITH SELF DO
            BEGIN
            typingCmd := NIL;
            styleCmdNumber := 0;
            newestLP := itsLP;
            nextHighTransit := hOnToOff;
            justReturned := FALSE;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TInsertionPoint.Free;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF SELF.typingCmd <> NIL THEN
            WITH SELF, typingCmd.typingRange DO
                BEGIN
                lastPara := textrange.firstPara;
                lastIndex := textrange.firstIndex;
                lastLP := textrange.firstLP;
                END;
        (*
        SELF.textRange.firstPara.StopEdit;
        *)
        SUPERSELF.Free;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    FUNCTION TInsertionPoint.Clone(heap: THeap): TObject;
    VAR insPt:  TInsertionPoint;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        insPt := TInsertionPoint(SUPERSELF.Clone(heap));
        insPt.typingCmd := NIL;
        insPt.amTyping := FALSE;
        Clone := insPt;
        {$IFC fTrace}EP;{$ENDC}
    END;



{$S SgTxtCld}
{$IFC fTextTrace}
    PROCEDURE TInsertionPoint.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        TTextSelection.Fields(Field);
        Field('typingCmd: TTypingCmd');
        Field('styleCmdNumber: INTEGER');
        Field('newestLP: INTEGER');
        Field('justReturned: BOOLEAN');
        Field('nextHighTransit: Byte');
        Field('nextTransitTime: LONGINT');
        Field('');
    END;
{$ENDC}


{$S SgTxtHot}
    FUNCTION TInsertionPoint.BecomeInsertionPoint: TInsertionPoint;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        BecomeInsertionPoint := SELF;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    FUNCTION TInsertionPoint.CanDoCommand(cmdNumber: TCmdNumber; VAR checkIt: BOOLEAN): BOOLEAN;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        CASE cmdNumber OF
            uCut, uCopy:
                CanDoCommand := FALSE;

            OTHERWISE
                CanDoCommand := SUPERSELF.CanDoCommand(cmdNumber, checkIt);
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TInsertionPoint.CutCopy(clipSelection: TSelection; deleteOriginal: BOOLEAN);
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        (* Staged Alert *)
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TInsertionPoint.FinishPaste(clipSelection: TSelection; pic: PicHandle);
    VAR clipTextSelection:  TMultiParaSelection;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        IF InClass(clipSelection, TMultiParaSelection) THEN
            BEGIN
            clipTextSelection := TMultiParaSelection(clipSelection);
            SELF.InsertText(clipTextSelection.textImage.text, clipTextSelection.isParaSelection,
                                                                clipTextSelection.isWordSelection, FALSE);
            END
        {$IFC fUseUnivText}
        ELSE IF clipBoard.hasUniversalText THEN
            SELF.InsertText(NIL, FALSE, FALSE, TRUE)
        {$ENDC};


        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TInsertionPoint.IdleBegin(centiSeconds: LONGINT);
    {Assumes highlighting is already on}
    BEGIN
        {$IFC fTrace}BP(6);{$ENDC}
        IF (SELF.kind = nothingKind) OR SELF.isParaSelection THEN
            SELF.nextHighTransit := hNone
        ELSE
            BEGIN
            SELF.textImage.text.HiliteRange(hOnToOff, SELF.textRange, FALSE);
            SELF.nextHighTransit := hOffTOOn;
            SELF.nextTransitTime := centiSeconds+blinkOffTime;
            END;
        SUPERSELF.IdleBegin(centiSeconds);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TInsertionPoint.IdleContinue(centiSeconds: LONGINT);
    BEGIN
        {$IFC fTrace}BP(6);{$ENDC}
        IF (SELF.nextHighTransit<>hNone) AND (centiSeconds > SELF.nextTransitTime) THEN
            BEGIN
            SELF.textImage.text.HiliteRange(SELF.nextHighTransit, SELF.textRange, FALSE);
            WITH SELF DO
                IF nextHighTransit = hOnToOff THEN
                    BEGIN
                    nextHighTransit := hOffToOn;
                    nextTransitTime := centiSeconds+blinkOffCentiSecs;
                    END
                ELSE
                    BEGIN
                    nextHighTransit := hOnToOff;
                    nextTransitTime := centiSeconds+blinkOnCentiSecs;
                    END;
                END;
        SUPERSELF.IdleContinue(centiSeconds);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TInsertionPoint.IdleEnd(centiSeconds: LONGINT);
        { end with highlighting on }
    BEGIN
        {$IFC fTrace}BP(6);{$ENDC}
        IF SELF.nextHighTransit=hOffToOn THEN
            SELF.textImage.text.HiliteRange(hOffToOn, SELF.textRange, FALSE);
        SELF.nextHighTransit := hNone;
        SUPERSELF.IdleEnd(centiSeconds);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TInsertionPoint.InsertText(text: TText; isParaSelection: BOOLEAN; isWordSelection: BOOLEAN;
                                                                                    universalText: BOOLEAN);
    VAR s:	        TListScanner;
        prevPara:       TEditPara;
        newPara:        TEditPara;
        aParagraph:     TEditPara;
        newLP:	        INTEGER;
        textImage:      TTextImage;
        insertIt:       BOOLEAN;
        done:	        BOOLEAN;
        newParaImage:   TParaImage;
        paraIndex:      LONGINT;
        delta:	        INTEGER;
        numParas:       INTEGER;
        needSpRight:    BOOLEAN;
        {$IFC fUseUnivText}
        readUnivText:   TTKReadUnivText;
        univPara:       TEditPara;
        univFormat:     TParaFormat;
        {$ENDC}

        PROCEDURE StartPaste;
        BEGIN
            {$IFC fTrace}BP(10);{$ENDC}
            IF universalText THEN
                BEGIN
                {$IFC fUseUnivText}
                univFormat := TParaFormat.CREATE(NIL, SELF.Heap, SELF.textImage.text.styleSheet);
                univPara := textImage.NewEditPara(0, prevPara.format);
                readUnivText := TTKReadUnivText.CREATE(NIL, SELF.Heap, NIL, 512,
                                                            [UTCharacters, UTParagraphs]);
                numParas := 0;
                {$ENDC}
                END
            ELSE
                BEGIN
                numParas := text.paragraphs.size;
                s := text.paragraphs.Scanner;
                END;
            {$IFC fTrace}EP;{$ENDC}
        END;

        PROCEDURE EndPaste;
        BEGIN
            {$IFC fTrace}BP(10);{$ENDC}
            IF universalText THEN
                BEGIN
                {$IFC fUseUnivText}
                univPara.Free;
                readUnivText.Free;
                {$ENDC}
                END;
            {$IFC fTrace}EP;{$ENDC}
        END;

        FUNCTION GetParagraph(VAR paragraph: TEditPara): BOOLEAN;
        VAR currPos:	    INTEGER;
            done:	    BOOLEAN;
            runSize:	    INTEGER;
            wasSomeText:    BOOLEAN;
            ch:		    CHAR;
            typeStyle:	    TTypeStyle;
        BEGIN
            {$IFC fTrace}BP(10);{$ENDC}
            If universalText THEN
                BEGIN
                {$IFC fUseUnivText}
                univPara.ReplPString(0, univPara.Size, NIL);
                currPos := 0;
                wasSomeText := FALSE;
                done := FALSE;
                REPEAT
                    readUnivText.ReadRun;
                    runSize := readUnivText.data.size;
                    IF runSize > 0 THEN
                        BEGIN
                        IF NOT wasSomeText THEN
                            BEGIN
                            WITH univFormat, readUnivText.paragraphDescriptor DO
                                BEGIN
                                firstIndent := firstLineMargin;
                                leftIndent := bodyMargin;
                                (* Can't use this because it's given as distance from left rather than
                                   indent from right and I don't know what value of right edge of paper is.
                                rightIndent := rightMargin;
                                *)
                                spaceBelowPara := paraLeading;
                                END;
                            univPara.format := univFormat;
                            END;
                        wasSomeText := TRUE;
                        ch := readUnivText.data.At(runSize);
                        IF ORD(ch) = ascReturn THEN
                            BEGIN
                            readUnivText.data.DelAt(runSize);
                            runSize := runSize - 1;
                            numParas := numParas + 1;
                            done := TRUE;
                            END;
                        univPara.ReplTString(currPos, 0, readUnivText.data, 0, runSize);
                        typeStyle.onFaces := readUnivText.characterDescriptor.face;
                        typeStyle.font.fontFamily := uvFont[readUnivText.characterDescriptor.font].fontFamily;
                        typeStyle.font.fontSize := uvFont[readUnivText.characterDescriptor.font].fontSize;
                        univPara.NewStyle(currPos, currPos+runSize, typeStyle);
                        currPos := currPos + runSize;
                        END
                    ELSE
                        BEGIN
                        IF wasSomeText THEN
                            numParas := numParas + 1;
                        done := TRUE;
                        END;
                UNTIL done;
                IF wasSomeText THEN
                    paragraph := univPara
                ELSE
                    paragraph := NIL;
                GetParagraph := wasSomeText;
                {$ELSEC}
                paragraph := NIL;
                GetParagraph := FALSE;
                {$ENDC}
                END
            ELSE
                GetParagraph := s.Scan(paragraph);
            {$IFC fTrace}EP;{$ENDC}
        END;

        PROCEDURE InsText;
        BEGIN
            {$IFC fTrace}BP(10);{$ENDC}
            delta := 0;
            textImage := SELF.textImage;
            newLP := SELF.textRange.firstLP;
            newPara := SELF.textRange.firstPara;
            prevPara := newPara;
            insertIt := FALSE;

            IF isWordSelection THEN
                BEGIN
                needSpRight := newPara.Qualifies(newLP);
                IF newPara.Qualifies(newLP-1) THEN
                    BEGIN
                    newPara.InsertOneChar(' ', newLP);
                    newLP := newLP + 1;
                    delta := 1;
                    END;
                END;

(*	    {special case: if first paragraph in text is designated a whole paragraph (by isParaSelection) AND
             if the insertion point (SELF) is at the end of the paragraph then we want to make a new
             paragraph rather than append it to the current paragraph and consequently set the flag that
             was supposed to prevent the first paragraph from being inserted}
            IF isParaSelection AND (prevPara.size = newLP) THEN
                BEGIN
                newPara := textImage.NewEditPara(0, prevPara.format);
                newLP := 0;
                insertIt := TRUE;
                END;
*)
            done := FALSE;
            StartPaste;
            IF GetParagraph(aParagraph) THEN
                BEGIN
                delta := delta + aParagraph.size;
                REPEAT
                newPara.ReplPara(newLP, 0, aParagraph, 0, aParagraph.size);
                newLP := newLP + aParagraph.size;
                IF insertIt THEN
                    textImage.text.InsParaAfter(prevPara, newPara);
                insertIt := TRUE;
                prevPara := newPara;
                IF GetParagraph(aParagraph) THEN
                    BEGIN
                    newPara := textImage.NewEditPara(prevPara.size-newLP,
                                                        TParaFormat(aParagraph.format.Clone(SELF.Heap)));
                    {For now, so we don't get garbage (if aParagraph later deleted), put cloned
                     format on to styleSheet list}
                    SELF.textImage.text.styleSheet.formats.InsLast(newPara.format);
                    newPara.StartEdit(newPara.GrowSize);
                    newPara.ReplPara(0, 0, prevPara, newLp, prevPara.size - newLp);
                    prevPara.ReplPString(newLp, prevPara.size-newLP, NIL);
                    prevPara.StopEdit;
                    newLP := 0;
                    END
                 ELSE
                    done := TRUE;
                UNTIL done;
                END;

            IF isParaSelection THEN
                BEGIN
                newPara := textImage.NewEditPara(prevPara.size - newLP, prevPara.format);
                newPara.StartEdit(newPara.GrowSize);
                newPara.ReplPara(0, 0, prevPara, newLp, prevPara.size - newLp);
                prevPara.ReplPString(newLp, prevPara.size - newLP, NIL);
                prevPara.StopEdit;
                textImage.text.InsParaAfter(prevPara, newPara);
                newPara := TEditPara(textImage.text.paragraphs.At(SELF.textRange.firstIndex + numParas));
                numParas := numParas+1;
                newLP := 0;
                END
            ELSE IF isWordSelection THEN
                IF needSpRight THEN
                    BEGIN
                    newPara.InsertOneChar(' ', newLP);
                    newLP := newLP + 1;
                    delta := delta + 1;
                    END;

            EndPaste;
            {$IFC fTrace}EP;{$ENDC}
        END;

        PROCEDURE Adjust;
            PROCEDURE AddDelta(paraImage: TParaImage);
            BEGIN
                paraImage.AdjustLineLPs(SELF.textRange.firstLP, delta);
            END;
        BEGIN
            {$IFC fTrace}BP(10);{$ENDC}
            SELF.textRange.firstPara.EachImage(AddDelta);

            WITH SELF, textRange DO
                BEGIN
                firstPara := newPara;
                lastPara := newPara;
                firstLP := newLP;
                lastLP := newLP;
                firstIndex := firstIndex + numParas - 1;
                lastIndex := firstIndex;
                newestLP := newLP;
                amTyping := FALSE;
                END;
            {$IFC fTrace}EP;{$ENDC}
        END;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        IF (text <> NIL) OR universalText THEN
            SELF.ChangeText(InsText, Adjust);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TInsertionPoint.KeyBack(fWord: BOOLEAN);
    VAR paragraph:      TEditPara;
        savedPara:      TEditPara;
        lp:	        INTEGER;
        typingCmd:      TTypingCmd;
        prevPara:       TEditPara;
        textRange:      TTextRange;

        PROCEDURE Adjust;
            VAR c:  CHAR;

            PROCEDURE DelOne(paraImage: TParaImage);
            BEGIN
                paraImage.AdjustLineLPs(lp, -1);
            END;
        BEGIN
            {$IFC fTrace}BP(10);{$ENDC}
            paragraph := SELF.textRange.firstPara;
            lp := SELF.newestLP;

            IF fWord THEN
                {&&& Move textRange.firstLP back to start of word};

            paragraph.BeginInsertion(lp, 0);

            IF (paragraph.holeStart < 1) AND (SELF.textRange.firstIndex = 1) THEN
                SELF.CantDoIt
            ELSE
                BEGIN
                prevPara := NIL;
                IF paragraph.holeStart < 1 THEN
                    BEGIN
                    {Backspacing over beginning of paragraph}
                    textRange := SELF.textRange;
                    textRange.firstIndex := textRange.firstIndex - 1;
                    prevPara := TEditPara(SELF.textImage.text.paragraphs.At(textRange.firstIndex));
                    WITH textRange DO
                        BEGIN
                        firstPara := prevPara;
                        firstLP := prevPara.size;
                        lastIndex := firstIndex;
                        lastPara := firstPara;
                        lastLP := firstLP;
                        SELF.newestLP := firstLP;
                        END;
                    prevPara.ReplPara(prevPara.size, 0, paragraph, 0, paragraph.size);
                    SELF.textImage.text.DelPara(paragraph, FALSE);
                    SELF.textImage.text.paragraphs.DelObject(paragraph, FALSE);
                    END
                ELSE
                    BEGIN
                    paragraph.bsCount := paragraph.bsCount + 1;
                    c := paragraph.At(paragraph.holeStart);
                    paragraph.DelAt(paragraph.holeStart);

                    paragraph.UpdateRuns(lp-1, 1, 0);
                    SELF.newestLP := lp-1;
                    END;

                typingCmd := SELF.typingCmd;
                IF prevPara = NIL THEN
                    BEGIN
                    typingCmd.newCharCount := typingCmd.newCharCount - 1;
                    IF typingCmd.newCharCount < 0 THEN
                        BEGIN
                        typingCmd.typingRange.firstLP := typingCmd.typingRange.firstLP - 1;
                        typingCmd.newCharCount := 0;
                        savedPara := TEditPara(typingCmd.savedText.paragraphs.First);
                        {$R-}
                        savedPara.InsertOneChar(c, 0);
                        {$IFC fRngText}{$R+}{$ENDC}
                        END;
                    END
                ELSE
                    BEGIN
                    typingCmd.newParaCount := typingCmd.newParaCount - 1;
                    IF typingCmd.newParaCount < 0 THEN
                        BEGIN
                        typingCmd.typingRange.firstIndex := textRange.firstIndex;
                        typingCmd.typingRange.firstPara := textRange.firstPara;
                        typingCmd.typingRange.firstLP := textRange.firstLP;
                        typingCmd.savedText.paragraphs.InsFirst(paragraph);
                        paragraph.ReplPString(0, paragraph.size, NIL);
                        typingCmd.newCharCount := 0;
                        typingCmd.newParaCount := 0;
                        END
                    ELSE
                        paragraph.Free;
                    END;


                deferUpdate := TRUE;
                SELF.justReturned := FALSE;
                END;
            {$IFC fTrace}EP;{$ENDC}
        END;

    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.KeyText;
        Adjust;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TInsertionPoint.KeyChar(ch: CHAR);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.KeyText;

        SELF.textRange.firstPara.InsertOneChar(ch, SELF.newestLP);

        IF SELF.styleCmdNumber <> 0 THEN
            BEGIN
            SELF.textRange.firstPara.NewStyle(SELF.newestLP, SELF.newestLP+1, SELF.currTypeStyle);
            SELF.styleCmdNumber := 0;
            END;
        WITH SELF.typingCmd DO
            newCharCount := newCharCount + 1;

        SELF.newestLP := SELF.newestLP + 1;
        SELF.justReturned := FALSE;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TInsertionPoint.KeyEnter;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.textImage.text.RecomputeImages;
        (*SELF.textImage.text.Invalidate;*)
        deferUpdate := FALSE;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TInsertionPoint.KeyForward(fWord: BOOLEAN);
    VAR paragraph:      TEditPara;
        savedPara:      TEditPara;
        lp:	        INTEGER;

    {NOTE: This first stab at KeyForward does NOT properly restore typestyles !!}
        PROCEDURE Adjust;

            PROCEDURE AddOne(paraImage: TParaImage);
            BEGIN
                paraImage.AdjustLineLPs(lp, 1);
            END;
        BEGIN
            paragraph := SELF.textRange.firstPara;
            lp := SELF.newestLP;

            IF (lp = paragraph.holeStart) AND (paragraph.bsCount > 0) THEN
                BEGIN

                IF fWord THEN
                    {&&& Recover word};

                WITH paragraph DO
                    BEGIN
                    bsCount := paragraph.bsCount - 1;
                    holeStart := paragraph.holeStart + 1;
                    holeSize := paragraph.holeSize - 1;
                    size := size + 1;
                    END;


                paragraph.UpdateRuns(lp-1, 0, 1);
                SELF.newestLP := lp+1;

                WITH SELF.typingCmd DO
                    newCharCount := newCharCount + 1;

                deferUpdate := TRUE;
                END;
            SELF.justReturned := FALSE;
        END;

    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.KeyText;
        Adjust;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TInsertionPoint.KeyPause;
    {Called by ToolKit when there are no keystrokes pending}
    VAR text:	    TText;
        diff:	    INTEGER;
        lp:	    INTEGER;
        textImage:  TTextImage;
        paraImage:  TParaImage;
        startLine:  INTEGER;
        startPixel: LONGINT;

        PROCEDURE AddDiff(paraImage: TParaImage);
        BEGIN
            paraImage.AdjustLineLPs(lp, diff);
        END;

        PROCEDURE AdjustOtherInsPts(obj: TObject);
        VAR insertPt:   TInsertionPoint;
        BEGIN
            insertPt := TInsertionPoint(obj);
            paraImage := insertPt.textImage.ImageWith(SELF.textRange.firstPara, lp);
            IF paraImage <> NIL THEN
                BEGIN
                paraImage.LocateLP(lp, startLine, startPixel);
                insertPt.textImage.firstLinePixel := startPixel;
                insertPt.textImage.useFirstPixel := TRUE;
                END
            ELSE
                insertPt.textImage.useFirstPixel := FALSE;

            WITH SELF, insertPt.textRange DO
                BEGIN
                firstLP := newestLP;
                lastLP := newestLP;
                IF firstIndex <> textRange.firstIndex THEN
                    BEGIN
                    firstIndex := textRange.firstIndex;
                    lastIndex := textRange.lastIndex;
                    firstPara := textRange.firstPara;
                    lastPara := textRange.lastPara;
                    END;
                END;
        END;

        PROCEDURE HiliteOtherInsPts(obj: TObject);
        VAR insertPt:   TInsertionPoint;
        BEGIN
            insertPt := TInsertionPoint(obj);
            IF insertPt.view.OKToDrawIn(insertPt.textImage.extentLRect) THEN
                insertPt.panel.Highlight(insertPt, hOffToOn);
        END;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF SELF.amTyping AND NOT SELF.justReturned THEN
            BEGIN
            textImage := SELF.textImage;
            text := textImage.text;
            diff := SELF.newestLP - SELF.textRange.firstLP;
            lp := Min(SELF.textRange.firstLP, SELF.newestLP);
            SELF.MarkChanged;
            SELF.textRange.firstPara.EachImage(AddDiff);

            {set up textImage fields for minimum rectangle erase of first update line}
            paraImage := textImage.ImageWith(SELF.textRange.firstPara, lp);
            IF paraImage <> NIL THEN
                BEGIN
                paraImage.LocateLP(lp, startLine, startPixel);
                textImage.firstLinePixel := startPixel;
                textImage.useFirstPixel := TRUE;
                END
            ELSE
                textImage.useFirstPixel := FALSE;

            SELF.textRange.firstLP := SELF.newestLP;
            SELF.textRange.lastLP := SELF.newestLP;
            IF SELF.typingCmd <> NIL THEN
                IF SELF.typingCmd.otherInsPts <> NIL THEN
                    SELF.typingCmd.otherInsPts.Each(AdjustOtherInsPts);

            text.RecomputeImages;

            {If view.OKToDrawIn was TRUE then we won't be going through the update cycle and hence the
             ToolKit won't tell us to highlight, so we'll have to highlight ourselves}
            IF SELF.view.OKToDrawIn(SELF.textImage.extentLRect) THEN
                SELF.panel.Highlight(SELF, hOffToOn);
            IF SELF.typingCmd <> NIL THEN
                IF SELF.typingCmd.otherInsPts <> NIL THEN
                    SELF.typingCmd.otherInsPts.Each(HiliteOtherInsPts);
            END;
        deferUpdate := FALSE;
        SELF.justReturned := FALSE;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    PROCEDURE TInsertionPoint.KeyReturn;
    VAR para1:	    TEditPara;
        newPara:    TEditPara;
        selSize:    INTEGER;

        PROCEDURE InsPara;
        VAR styleChange:    TStyleChange;
        BEGIN
            {$IFC fTrace}BP(10);{$ENDC}
            para1 := SELF.textRange.firstPara;
            selSize := para1.size - SELF.textRange.firstLP;
            newPara := SELF.textImage.NewEditPara(selSize, para1.format);
            newPara.StartEdit(newPara.GrowSize);
            newPara.ReplPara(0, 0, para1, SELF.textRange.firstLP, selSize);
            IF TFakeTStyle(newPara.format.dfltTStyle) <> TFakeTStyle(SELF.currTypeStyle) THEN
                BEGIN
                styleChange.lp := -1;
                styleChange.newStyle := SELF.currTypeStyle;
                newPara.typeStyles.PutAt(1, @styleChange);
                END;
            para1.ReplPString(SELF.textRange.firstLP, selSize, NIL);
            para1.StopEdit;
            SELF.textImage.text.InsParaAfter(para1, newPara);
            {$IFC fTrace}EP;{$ENDC}
        END;

        PROCEDURE Adjust;
            PROCEDURE AdjustOtherInsPts(obj: TObject);
            VAR insertPt:   TInsertionPoint;
            BEGIN
                insertPt := TInsertionPoint(obj);
                WITH insertPt, textRange DO
                    BEGIN
                    firstLP := 0;
                    lastLP := 0;
                    firstIndex := SELF.textRange.firstIndex;
                    lastIndex := firstIndex;
                    firstPara := newPara;
                    lastPara := newPara;
                    END;
            END;

        BEGIN
            {$IFC fTrace}BP(10);{$ENDC}
            WITH SELF.textRange DO
                BEGIN
                firstPara := newPara;
                lastPara := newPara;
                firstIndex := firstIndex + 1;
                lastIndex := firstIndex;
                firstLP := 0;
                lastLP := 0;
                END;
            SELF.newestLP := 0;
            SELF.justReturned := TRUE;
            WITH SELF.typingCmd DO
                newParaCount := newParaCount + 1;
            IF SELF.typingCmd.otherInsPts <> NIL THEN
                SELF.typingCmd.otherInsPts.Each(AdjustOtherInsPts);
            deferUpdate := FALSE;
            SELF.textImage.useFirstPixel := FALSE;
            {$IFC fTrace}EP;{$ENDC}
        END;

        PROCEDURE HiliteOtherInsPts(obj: TObject);
        VAR insertPt:   TInsertionPoint;
        BEGIN
            insertPt := TInsertionPoint(obj);
            IF insertPt.view.OKToDrawIn(insertPt.textImage.extentLRect) THEN
                insertPt.panel.Highlight(insertPt, hOffToOn);
        END;

    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.KeyText;
        SELF.ChangeText(InsPara, Adjust);
        {If view.OKToDrawIn was TRUE then we won't be going through the update cycle and hence the
         ToolKit won't tell us to highlight, so we'll have to highlight ourselves}
        IF SELF.view.OKToDrawIn(SELF.textImage.extentLRect) THEN
            SELF.panel.Highlight(SELF, hOffToOn);
        IF SELF.typingCmd.otherInsPts <> NIL THEN
            SELF.typingCmd.otherInsPts.Each(HiliteOtherInsPts);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TInsertionPoint.KeyTab;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.KeyChar(' ');
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TInsertionPoint.MarkChanged;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF SELF.newestLP < SELF.textRange.firstLP THEN
            BEGIN
            SELF.textRange.firstLP := SELF.newestLP;
            SELF.textRange.lastLP := SELF.newestLP;
            SUPERSELF.MarkChanged;
            END
        ELSE
            BEGIN
            SUPERSELF.MarkChanged;
            SELF.textRange.firstLP := SELF.newestLP;
            SELF.textRange.lastLP := SELF.newestLP;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TInsertionPoint.MouseMove(mouseLPt: LPoint);
    VAR currPImage:	    TParaImage;
        currLP:		    INTEGER;
        multiParaSelection: TMultiParaSelection;
        oneParaSelection:   TOneParaSelection;
        textImage:	    TTextImage;
        firstTxtImg:	    TTextImage;
        paraIndex:	    LONGINT;
        wasParaSel:	    BOOLEAN;

    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.currLPt := mouseLPt;
        IF NOT EqualLPt(mouseLPt, SELF.anchorLPt) THEN
            BEGIN
            textImage := SELF.textImage.FindTextImage(mouseLPt, firstTxtImg);
            textImage.FindParaAndLp(mouseLPt, currPImage, paraIndex, currLP);

            IF (currPImage.paragraph <> SELF.textRange.firstPara) THEN
                BEGIN
                {Turn insertion point off if necessary}
                wasParaSel := SELF.isParaSelection;
                IF NOT wasParaSel AND (paraIndex > SELF.textRange.firstIndex) THEN
                    SELF.textImage.text.HiliteRange(hOnToOff, SELF.textRange, FALSE);
                {call new with same paragraph, followed by MouseMove so highlighting will work correctly
                 and so MouseMove can figure out if currPImage is before of after SELF.textRange.firstPara}
                WITH SELF, textRange DO
                    {$H-} {ought to be safe: no VAR params, etc}
                    multiParaSelection := TMultiParaSelection(SELF.FreedAndReplacedBy(
                                            TMultiParaSelection.CREATE(NIL, SELF.Heap,
                                                view, firstTxtImg, anchorLPt,
                                                firstPara, firstIndex, firstLP,
                                                firstPara, firstIndex, firstLP, TRUE)));
                    {$H+}
                multiParaSelection.isParaSelection := wasParaSel;
                multiParaSelection.MouseMove(mouseLPt);
                END
            ELSE IF (currLP <> SELF.textRange.firstLP) THEN
                BEGIN
                {Turn insertion point off first}
                SELF.textImage.text.HiliteRange(hOnToOff, SELF.textRange, FALSE);
                {call CREATE with same LP for begin and end,
                    followed by MouseMove so highlighting will work correctly}
                WITH SELF, textRange DO
                    {$H-} {ought to be safe: no VAR params, etc}
                    oneParaSelection := TOneParaSelection(SELF.FreedAndReplacedBy(
                                            TOneParaSelection.CREATE(NIL, SELF.Heap,
                                                view, firstTxtImg, anchorLPt,
                                                firstPara, firstIndex, firstLP, firstLP)));
                    {$H+}
                oneParaSelection.MouseMove(mouseLPt);
                END;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    PROCEDURE TInsertionPoint.MousePress(mouseLPt: LPoint);
    VAR first, last:	    INTEGER;
        oneParaSelection:   TOneParaSelection;
        funnnyInsPoint:	    TInsertionPoint;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF clickState.fShift THEN
            SELF.MouseMove(mouseLPt)
        ELSE IF clickState.clickCount = 2 THEN  {double click}
            BEGIN
            {This check should solve the problem of double clicking on the last half of the last
             character of a word and not getting the word selected}
            WITH SELF.textRange DO
                BEGIN
                first := firstLP;
                {$H-}
                IF NOT firstPara.Qualifies(first) THEN
                    IF first > 0 THEN
                        first := first - 1;
                {$H+}
                END;
            SELF.textRange.firstPara.FindWordBounds(first, first, last);
            IF first <> last THEN
                BEGIN
                {Turn insertion point off first}
                SELF.textImage.text.HiliteRange(hOnToOff, SELF.textRange, FALSE);
                WITH SELF, textRange DO
                    {$H-}
                    oneParaSelection := TOneParaSelection(SELF.FreedAndReplacedBy(
                                            TOneParaSelection.CREATE(NIL, SELF.Heap,
                                                view, textImage, anchorLPt,
                                                firstPara, firstIndex, first, last)));
                    {$H+}
                WITH oneParaSelection DO
                    BEGIN
                    anchorbegin := first;
                    anchorEnd := last;
                    isWordSelection := TRUE;
                    END;
                oneParaSelection.textImage.text.HiliteRange(hOffToOn, oneParaSelection.textRange, FALSE);
                END;
            END
        ELSE IF clickState.clickCount = 3 THEN  {triple click, happens if double click didn't select word}
            BEGIN
            {Turn insertion point off first}
            SELF.textImage.text.HiliteRange(hOnToOff, SELF.textRange, SELF.isParaSelection);

            IF SELF.textRange.firstPara.size = 0 THEN
            {This case precipitates the odd notion of an "insertion point" the width of the textImage;
             (ie. when one triple clicks on an empty paragraph)	 A special check in IdleBegin makes sure
             it doesn't blink.}
                BEGIN
                WITH SELF, textRange DO
                    {$H-}
                    funnnyInsPoint := TInsertionPoint(SELF.FreedAndReplacedBy(
                                            TInsertionPoint.CREATE(NIL, SELF.Heap,
                                                view, textImage, anchorLPt,
                                                firstPara, firstIndex, 0)));
                    {$H+}
                WITH funnnyInsPoint DO
                    BEGIN
                    isParaSelection := TRUE;
                    isWordSelection := FALSE;
                    END;
                funnnyInsPoint.textImage.text.HiliteRange(hOffToOn, SELF.textRange, TRUE);
                END
            ELSE
                BEGIN
                WITH SELF, textRange DO
                    {$H-}
                    oneParaSelection := TOneParaSelection(SELF.FreedAndReplacedBy(
                                            TOneParaSelection.CREATE(NIL, SELF.Heap,
                                                view, textImage, anchorLPt,
                                                firstPara, firstIndex, 0, firstPara.size)));
                    {$H+}
                WITH oneParaSelection DO
                    BEGIN
                    isParaSelection := TRUE;
                    isWordSelection := FALSE;
                    anchorBegin := 0;
                    anchorEnd := textRange.lastLP;
                    END;
                oneParaSelection.textImage.text.HiliteRange(hOffToOn, oneParaSelection.textRange, TRUE);
                END;
            END
        ELSE
            SELF.textImage.MousePress(mouseLPt);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TInsertionPoint.NewStyleCmd(heap: THeap; cmdNumber: TCmdNumber;
                                    textImage: TTextImage): TCommand;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        {Do stuff but set NewStyleCmd NIL so last cmd not committed};
        SELF.styleCmdNumber := cmdNumber;
        WITH SELF.currTypeStyle, font DO
            CASE cmdNumber OF
                uPlain:
                    onFaces := [];
                uBold:
                    onFaces := onFaces + [bold];
                uItalic:
                    onFaces := onFaces + [italic];
                uUnderline:
                    onFaces := onFaces + [underlined];
                uShadow:
                    onFaces := onFaces + [shadow];
                uOutline:
                    onFaces := onFaces + [outline];
                uModern, uClassic:
                    font.fontFamily := cmdNumber - uModern + 1;

                {$IFC LibraryVersion <= 20}
                uFnt0, uFnt1, uFnt2, uFnt3, uFnt4, uFnt5, uFnt6, uFnt7, uFnt8, uFnt9, uFnt10, uFnt11:
                    CASE cmdNumber OF
                        uFnt0:	 fontFamily := famSystem;
                        uFnt1:
                            BEGIN
                            fontFamily := famModern;
                            fontSize := size15Pitch;
                            END;
                        uFnt2:
                            BEGIN
                            fontFamily := famModern;
                            fontSize := size12Pitch;
                            END;
                        uFnt3:
                            BEGIN
                            fontFamily := famClassic;
                            fontSize := size12Pitch;
                            END;
                        uFnt4:
                            BEGIN
                            fontFamily := famModern;
                            fontSize := size10Pitch;
                            END;
                        uFnt5:
                            BEGIN
                            fontFamily := famModern;
                            fontSize := size14Point;
                            END;
                        uFnt6:
                            BEGIN
                            fontFamily := famModern;
                            fontSize := size12Point;
                            END;
                        uFnt7:
                            BEGIN
                            fontFamily := famClassic;
                            fontSize := size12Point;
                            END;
                        uFnt8:
                            BEGIN
                            fontFamily := famModern;
                            fontSize := size18Point;
                            END;
                        uFnt9:
                            BEGIN
                            fontFamily := famClassic;
                            fontSize := size18Point;
                            END;
                        uFnt10:
                            BEGIN
                            fontFamily := famModern;
                            fontSize := size24Point;
                            END;
                        uFnt11:
                            BEGIN
                            fontFamily := famClassic;
                            fontSize := size24Point;
                            END;
                        END;
                {$ENDC}

                OTHERWISE
                    font.fontSize := cmdNumber - u20Pitch + 1;
                END;
        NewStyleCmd := NIL;
        {$IFC fTrace}EP;{$ENDC}
    END;



{$S SgTxtCld}
    FUNCTION TInsertionPoint.NewCutCopyCmd(heap: THeap; cmdNumber: TCmdNumber;
                                    textImage: TTextImage): TCommand;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        {don't create a new command object for insertion point style change}
        NewCutCopyCmd := NIL;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TInsertionPoint.SelSize: INTEGER;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        SelSize := 0;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TInsertionPoint.StyleFromContext;
    VAR typeStyle:      TTypeSTyle;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        SELF.textRange.firstPara.StyleAt(Max(SELF.textRange.firstLP - 1, 0), typeStyle);
        SELF.currTypeStyle := typeStyle;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtIni}
BEGIN
    {temp patch to get fonts from universal text}
    uvFont[4].fontFamily := famModern;
    uvFont[5].fontFamily := famModern;
    uvFont[6].fontFamily := famModern;
    uvFont[7].fontFamily := famModern;
    uvFont[8].fontFamily := famModern;
    uvFont[9].fontFamily := famModern;
    uvFont[10].fontFamily := famClassic;
    uvFont[11].fontFamily := famClassic;
    uvFont[12].fontFamily := famClassic;
    uvFont[13].fontFamily := famClassic;
    uvFont[14].fontFamily := famClassic;
    uvFont[4].fontSize := 5;
    uvFont[5].fontSize := 7;
    uvFont[6].fontSize := 8;
    uvFont[7].fontSize := 2;
    uvFont[8].fontSize := 3;
    uvFont[9].fontSize := 4;
    uvFont[10].fontSize := 5;
    uvFont[11].fontSize := 7;
    uvFont[12].fontSize := 8;
    uvFont[13].fontSize := 3;
    uvFont[14].fontSize := 6;
END; {Methods of TInsertionPoint}


METHODS OF TOneParaSelection;

{$S SgTxtHot}
    FUNCTION  TOneParaSelection.CREATE(object: TObject; heap: THeap; itsView: TView; itsTextImage: TTextImage;
                                       itsAnchorLPt: LPoint; itsParagraph: TEditPara; itsIndex: LONGINT;
                                       oldLP: INTEGER; currLP: INTEGER): TOneParaSelection;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF object = NIL THEN
            object := NewObject(heap, THISCLASS);
        SELF := TOneParaSelection(TTextSelection.CREATE(object, heap, itsView, itsTextImage, itsAnchorLPt,
                                            itsParagraph, itsIndex, Min(oldLP, currLP),
                                            itsParagraph, itsIndex, Max(oldLP, currLP)));

        WITH SELF DO
            BEGIN
            anchorBegin := oldLP;
            anchorEnd := oldLP;
            viewTick := -1; { force recalculation of extent }
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
{$IFC fTextTrace}
    PROCEDURE TOneParaSelection.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        TTextSelection.Fields(Field);
        Field('anchorBegin: INTEGER');
        Field('anchorEnd: INTEGER');
        Field('');
    END;
{$ENDC}

{$S SgTxtCld}
    PROCEDURE TOneParaSelection.ChangeStyle(cmdNumber: TCmdNumber);
    VAR newTypeStyle:   TTypeStyle;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        WITH SELF.textRange DO
            {$H-}
            SELF.DoChangeStyle(cmdNumber, firstPara, firstLP, lastLP, newTypeStyle);
            {$H+}
        SELF.currTypeStyle := newTypeStyle;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    {CopySelf is used for copying to the clipboard}
    FUNCTION TOneParaSelection.CopySelf(heap: THeap; view: TView): TMultiParaSelection;
    VAR paragraph:      TEditPara;
        selSize:        INTEGER;
        newImage:       TParaImage;
        lastLine:       TLineInfo;
        textImage:      TTextImage;
        text:	        TText;
        imageLRect:     LRect;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        text := TText.CREATE(NIL, heap, TStyleSheet.CREATE(NIL, heap));
        imageLRect := view.extentLRect;
        InsetLRect(imageLRect, cHorizMargin, cVertMargin);
        textImage := SELF.textImage.TxtImgForClipBoard(heap, view, imageLRect, text, TRUE);
        textImage.minHeight := 0;
        text.txtImgList.InsLast(textImage);

        selSize := SELF.textRange.lastLP-SELF.textRange.firstLP;
        paragraph := textImage.NewEditPara(selSize, TParaFormat(SELF.textRange.firstPara.format.Clone(heap)));
        paragraph.ReplPara(0, 0, SELF.textRange.firstPara, SELF.textRange.firstLP, selSize);

        newImage := textImage.NewParaImage(paragraph, textImage.extentLRect, 0, 0);
        textImage.imageList.InsLast(newImage);
        textImage.text.paragraphs.InsLast(paragraph);


        { make view extentLRect exactly fit the lines }
        textImage.RecomputeImages(actionNone, TRUE);
        WITH textImage.extentLRect DO
            view.extentLRect.bottom := bottom - top + 2 * cVertMargin;

        CopySelf := TMultiParaSelection.CREATE(NIL, heap, view, textImage, zeroLPt, paragraph, 1, 0,
                                                paragraph, 1, selSize, TRUE);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtWrm}
    PROCEDURE TOneParaSelection.DeleteAndFree;
    VAR delta:  INTEGER;

        PROCEDURE DelText;
        BEGIN
            IF SELF.isParaSelection AND
                        (SELF.textRange.firstIndex <> SELF.textImage.text.paragraphs.size) THEN
                BEGIN
                SELF.textImage.text.DelPara(SELF.textRange.firstPara, FALSE);
                SELF.textImage.text.paragraphs.DelObject(SELF.textRange.firstPara, TRUE);
                END
            ELSE
                SELF.textRange.firstPara.ReplPString(SELF.textRange.firstLP,
                                SELF.textRange.lastLP-SELF.textRange.firstLP, NIL);
        END;

        PROCEDURE Adjust;
            PROCEDURE Subtract(paraImage: TParaImage);
            BEGIN
                paraImage.AdjustLineLPs(SELF.textRange.firstLP, delta);
            END;
        BEGIN
            IF NOT SELF.isParaSelection THEN
                BEGIN
                delta := SELF.textRange.firstLP - SELF.textRange.lastLP;
                SELF.textRange.firstPara.EachImage(Subtract);
                END;
            WITH SELF.textRange DO
                BEGIN
                IF SELF.isParaSelection THEN
                    BEGIN
                    {$H-}
                    firstPara := TEditPara(SELF.textImage.text.paragraphs.At(firstIndex));
                    {$H+}
                    firstLP := 0;
                    lastPara := firstPara;
                    END;
                lastLP := firstLP;
                END;
        END;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.ChangeText(DelText, Adjust);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    FUNCTION  TOneParaSelection.DeleteButSave: TText;
    VAR oldPara:    TEditPara;
        selSize:    INTEGER;
        newPara:    TEditPara;
        text:	    TText;
        PROCEDURE DelText;
        BEGIN
            oldPara := SELF.textRange.firstPara;
            IF SELF.isParaSelection AND
                        (SELF.textRange.firstIndex <> SELF.textImage.text.paragraphs.size) THEN
                BEGIN
                SELF.textImage.text.DelPara(oldPara, FALSE);
                SELF.textImage.text.paragraphs.DelObject(oldPara, FALSE);
                newPara := oldPara;
                END
            ELSE
                BEGIN
                selSize := SELF.textRange.lastLP-SELF.textRange.firstLP;
                newPara := SELF.textImage.NewEditPara(selSize, oldPara.format);
                newPara.ReplPara(0, 0, oldPara, SELF.textRange.firstLP, selSize);
                oldPara.ReplPString(SELF.textRange.firstLP, selSize, NIL);
                END;
            text := TText.CREATE(NIL, SELF.Heap, NIL);
            text.paragraphs.InsLast(newpara);
        END;

        PROCEDURE Adjust;
            PROCEDURE Subtract(paraImage: TParaImage);
            BEGIN
                paraImage.AdjustLineLPs(SELF.textRange.firstLP, -selSize);
            END;
        BEGIN
            WITH SELF.textRange DO
                BEGIN
                IF SELF.isParaSelection THEN
                    BEGIN
                    {$H-}
                    firstPara := TEditPara(SELF.textImage.text.paragraphs.At(firstIndex));
                    {$H+}
                    firstLP := 0;
                    lastPara := firstPara;
                    END;
                lastLP := firstLP;
                END;
            IF NOT SELF.isParaSelection THEN
                SELF.textRange.firstPara.EachImage(Subtract);
        END;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.ChangeText(DelText, Adjust);
        DeleteButSave := text;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TOneParaSelection.MouseMove(mouseLPt: LPoint);
        { assumes highlighting is ON }
    VAR currPImage:	    TParaImage;
        currLP:		    INTEGER;
        oldLP:		    INTEGER;    { end (ie. not the anchor) of indication }
        multiParaSelection: TMultiParaSelection;
        textImage:	    TTextImage;
        firstTxtImg:	    TTextImage;
        first, last:	    INTEGER;
        paraSelect:	    BOOLEAN;
        paraIndex:	    LONGINT;

        PROCEDURE HiExtOnPads(highTransit: THighTransit; startLP,endLP: INTEGER);
        BEGIN
            SELF.textImage.text.HiliteParagraphs(highTransit, SELF.textRange.firstIndex, startLP,
                                                              SELF.textRange.firstIndex, endLP,
                                                              SELF.isParaSelection);
        END;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.currLPt := mouseLPt;

        textImage := SELF.textImage.FindTextImage(mouseLPt, firstTxtImg);
        textImage.FindParaAndLp(mouseLPt, currPImage, paraIndex, currLP);

        IF currPImage.paragraph <> SELF.textRange.firstPara THEN
            BEGIN
            {call new with same paraImage, followed by MouseMove so highlighting will work correctly}
            paraSelect := SELF.isParaSelection;
            first := SELF.anchorBegin;
            last := SELF.anchorEnd;
            WITH SELF, textRange DO
                {$H-}
                multiParaSelection := TMultiParaSelection(SELF.FreedAndReplacedBy(
                                        TMultiParaSelection.CREATE(NIL, SELF.Heap,
                                            view, firstTxtImg, anchorLPt,
                                            firstPara, firstIndex, firstLP,
                                            firstPara, firstIndex, lastLP, firstLP = anchorBegin)));
                {$H+}
            WITH multiParaSelection DO
                BEGIN
                isParaSelection := paraSelect;
                anchorBegin := first;
                anchorEnd := last;
                isWordSelection := NOT paraSelect AND (first <> last);
                END;
            multiParaSelection.MouseMove(mouseLPt);
            END
        ELSE IF NOT SELF.isParaSelection THEN
            BEGIN
            IF SELF.isWordSelection THEN
                BEGIN
                {So dragging over last half of last character of word doesn't select space}
                WITH SELF.textRange DO
                    BEGIN
                    {$H-}
                    IF NOT firstPara.Qualifies(currLP) THEN
                        IF currLP > 0 THEN
                            currLP := currLP - 1;
                    {$H+}
                    END;
                SELF.textRange.firstPara.FindWordBounds(currLP, first, last);
                IF first <= SELF.anchorBegin THEN
                    currLP := first
                ELSE
                    currLP := last;
                END;

            IF currLP <= SELF.anchorBegin THEN
                BEGIN
                oldLP := SELF.textRange.firstLP;
                IF SELF.anchorEnd <> SELF.textRange.lastLP THEN
                    HiExtOnPads(hOnToOff, SELF.anchorEnd, SELF.textRange.lastLP);
                SELF.textRange.firstLP := currLP;
                SELF.textRange.lastLP := SELF.anchorEnd;
                END
            ELSE
                BEGIN
                oldLP := SELF.textRange.lastLP;
                IF SELF.anchorBegin <> SELF.textRange.firstLP THEN
                    HiExtOnPads(hOnToOff, SELF.textRange.firstLP, SELF.anchorBegin);
                SELF.textRange.firstLP := SELF.anchorBegin;
                SELF.textRange.lastLP := currLP;
                END;

            IF currLP <> oldLP THEN
                IF currLP < oldLP THEN
                    HiExtOnPads(hOffToOn, currLP, oldLP)
                ELSE
                    HiExtOnPads(hOffToOn, oldLP, currLP);

            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TOneParaSelection.MousePress(mouseLPt: LPoint);
    VAR first, last:	    INTEGER;
        oneParaSelection:   TOneParaSelection;
        multiParaSelection: TMultiParaSelection;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF clickState.fShift THEN
            SELF.MouseMove(mouseLPt)
        ELSE IF clickState.clickCount = 2 THEN  {double click}
            BEGIN
            {should select words at beginning and end of current selection (later)}
            SELF.textRange.firstPara.FindWordBounds(SELF.anchorBegin, first, last);
            SELF.textImage.text.HiliteRange(hOnToOff, SELF.textRange, SELF.isParaSelection);
            WITH SELF, textRange DO
                {$H-}
                oneParaSelection := TOneParaSelection(SELF.FreedAndReplacedBy(
                                        TOneParaSelection.CREATE(NIL, SELF.Heap,
                                            view, textImage, anchorLPt,
                                            firstPara, firstIndex, first, last)));
                {$H+}
            WITH oneParaSelection DO
                BEGIN
                anchorBegin := first;
                anchorEnd := last;
                isWordSelection := TRUE;
                END;
            oneParaSelection.textImage.text.HiliteRange(hOnToOff, oneParaSelection.textRange,
                                                                            SELF.isParaSelection);
            END
        ELSE IF clickState.clickCount = 3 THEN  {triple click}
            BEGIN
            {Turn current highlighting off first}
            SELF.textImage.text.HiliteRange(hOnToOff, SELF.textRange, SELF.isParaSelection);
            WITH SELF, textRange DO
                {$H-}
                oneParaSelection := TOneParaSelection(SELF.FreedAndReplacedBy(
                                        TOneParaSelection.CREATE(NIL, SELF.Heap,
                                            view, textImage, anchorLPt,
                                            firstPara, firstIndex, 0, firstPara.size)));
                {$H+}
            WITH oneParaSelection DO
                BEGIN
                isParaSelection := TRUE;
                isWordSelection := FALSE;
                anchorBegin := 0;
                anchorEnd := textRange.lastLP;
                END;
            oneParaSelection.textImage.text.HiliteRange(hOnToOff, oneParaSelection.textRange, TRUE);
            END
        ELSE
            SELF.textImage.MousePress(mouseLPt);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TOneParaSelection.MouseRelease;
    VAR insPt:  TInsertionPoint;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF SELF.textRange.firstLP = SELF.textRange.lastLP THEN
            BEGIN
            insPt := SELF.BecomeInsertionPoint;
            insPt.textImage.text.HiliteRange(hOffToOn, insPt.textRange, SELF.isParaSelection);
            END
        ELSE
            SELF.textImage.text.ChangeSelInOtherPanels(SELF);
        SUPERSELF.MouseRelease;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TOneParaSelection.SelSize: INTEGER;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        SelSize := SELF.textRange.lastLP - SELF.textRange.firstLP;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtHot}
    PROCEDURE TOneParaSelection.StyleFromContext;
    VAR typeStyle:  TTypeStyle;
    BEGIN
        {$IFC fTrace}BP(8);{$ENDC}
        SELF.textRange.firstPara.StyleAt(SELF.textRange.firstLP, typeStyle);
        SELF.currTypeStyle := typeStyle;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtIni}
END; {Methods of TOneParaSelection}


METHODS OF TMultiParaSelection;

{$S SgTxtCld}
    FUNCTION TMultiParaSelection.CREATE(object: TObject; heap: THeap; itsView: TView;
                                        itsTextImage: TTextImage; itsAnchorLPt: LPoint;
                                        beginPara: TEditPara; beginIndex: LONGINT; beginLP: INTEGER;
                                        endPara: TEditPara; endIndex: LONGINT; endLP: INTEGER;
                                        beginIsAnchor: BOOLEAN): TMultiParaSelection;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF object = NIL THEN
            object := NewObject(heap, THISCLASS);
        SELF := TMultiParaSelection(TTextSelection.CREATE(object, heap, itsView, itsTextImage, itsAnchorLPt,
                                            beginPara, beginIndex, beginLP,
                                            endPara, endIndex, endLP));


        WITH SELF DO
            BEGIN
            IF beginIsAnchor THEN
                BEGIN
                anchorPara := beginPara;
                anchorIndex := beginIndex;
                anchorBegin := beginLP;
                anchorEnd := beginLP;
                END
            ELSE
                BEGIN
                anchorPara := endPara;
                anchorIndex := endIndex;
                anchorBegin := endLP;
                anchorEnd := endLP;
                END;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtCld}
{$IFC fTextTrace}
    PROCEDURE TMultiParaSelection.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        TTextSelection.Fields(Field);
        Field('anchorPara: TEditPara');
        Field('anchorIndex: LONGINT');
        Field('anchorBegin: INTEGER');
        Field('anchorEnd: INTEGER');
        Field('');
    END;
{$ENDC}


{$S SgTxtCld}
    PROCEDURE TMultiParaSelection.ChangeStyle(cmdNumber: TCmdNumber);
    VAR newTypeStyle:   TTypeStyle;
        s:	        TListScanner;
        paragraph:      TEditPara;
        lastPara:       TEditPara;
        endRng:	        INTEGER;
        paraImage:      TParaImage;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        newTypeStyle := SELF.currTypeStyle;
        lastPara := SELF.textRange.lastPara;
        s := SELF.textImage.text.paragraphs.ScannerFrom(SELF.textRange.firstIndex-1, scanForward);
        WHILE s.Scan(paragraph) DO
            IF paragraph = SELF.textRange.firstPara THEN
                BEGIN
                SELF.DoChangeStyle(cmdNumber, paragraph, SELF.textRange.firstLP,
                                        paragraph.size, newTypeStyle);
                SELF.currTypeStyle := newTypeStyle;
                IF paragraph = lastPara THEN
                    s.Done;
                END
            ELSE
                BEGIN
                IF paragraph = lastPara THEN
                    BEGIN
                    endRng := SELF.textRange.lastLP;
                    s.Done;
                    END
                ELSE
                    endRng := paragraph.size;

                SELF.DoChangeStyle(cmdNumber, paragraph, 0, endRng, newTypeStyle);
                END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    {CopySelf is used for copying to the clipboard}
    FUNCTION TMultiParaSelection.CopySelf(heap: THeap; view: TView): TMultiParaSelection;
    VAR srcPara:	    TEditPara;
        cpyPara:	    TEditPara;
        cpyFirstPara:	    TEditPara;
        srcLastPara:	    TEditPara;
        cpyLastPara:	    TEditPara;
        selSize1:	    INTEGER;
        selSize2:	    INTEGER;
        textImage:	    TTextImage;
        s:		    TListScanner;
        text:		    TText;
        textRange:	    TTextRange;
        imageLRect:	    LRect;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        text := TText.CREATE(NIL, heap, TStyleSheet.CREATE(NIL, heap));
        imageLRect := view.extentLRect;
        InsetLRect(imageLRect, cHorizMargin, cVertMargin);
        textImage := SELF.textImage.TxtImgForClipBoard(heap, view, imageLRect, text, TRUE);
        textImage.minHeight := 0;
        text.txtImgList.InsLast(textImage);

        textRange := SELF.textRange;
        srcPara := textRange.firstPara;
        selSize1 := srcPara.size-textRange.firstLP;
        cpyPara := textImage.NewEditPara(selSize1, TParaFormat(srcPara.format.clone(heap)));
        cpyPara.ReplPara(0, 0, srcPara, textRange.firstLP, selSize1);
        cpyFirstPara := cpyPara;

        textImage.text.paragraphs.InsLast(cpyFirstPara);

        IF textRange.firstPara <> textRange.lastPara THEN
            BEGIN
            srcLastPara := textRange.lastPara;
            selSize2 := textRange.lastLP;
            cpyLastPara := textImage.NewEditPara(selSize2, TParaFormat(srcLastPara.format.clone(heap)));
            cpyLastPara.ReplPara(0, 0, srcLastPara, 0, selSize2);

            {skip first paragraph by not subtracting one from firstIndex}
            s := SELF.textImage.text.paragraphs.ScannerFrom(textRange.firstIndex, scanForward);
            WHILE s.Scan(srcPara) DO
                BEGIN
                IF srcPara = textRange.lastPara THEN
                    BEGIN
                    cpyPara := cpyLastPara;
                    s.Done;
                    END
                ELSE
                    BEGIN
                    cpyPara := textImage.NewEditPara(srcPara.size,
                                        TParaFormat(srcPara.format.clone(heap)));
                    cpyPara.ReplPara(0, 0, srcPara, 0, srcPara.size);
                    END;

                textImage.text.paragraphs.InsLast(cpyPara);
                END;
            END;

        textImage.RecomputeImages(actionNone, TRUE);
        WITH textImage.extentLRect DO
            view.extentLRect.bottom := bottom - top + 2 * cVertMargin;

        CopySelf := TMultiParaSelection.CREATE(NIL, heap, view, textImage, zeroLPt,
                                            TEditPara(textImage.text.paragraphs.First), 1, 0,
                                            TEditPara(textImage.text.paragraphs.Last),
                                            textRange.lastIndex - textRange.firstIndex + 1,
                                            selSize2, TRUE);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TMultiParaSelection.Delete(saveIt: BOOLEAN): TText;
    VAR firstPara:	    TEditPara;
        lastPara:	    TEditPara;
        paragraph:	    TEditPara;
        text:		    TText;
        paraList:	    TList;
        s:		    TListScanner;
        selSize:	    INTEGER;
        textRange:	    TTextRange;
        numParas:	    INTEGER;

        PROCEDURE DelText;
        BEGIN
            {$IFC fTrace}BP(11);{$ENDC}
            {If saveIt is TRUE, we want to save the text we're deleting, so create a text object}
            IF saveIt THEN
                BEGIN
                text := TText.CREATE(NIL, SELF.Heap, NIL);
                paraList := text.paragraphs;
                END
            ELSE
                text := NIL;

            textRange := SELF.textRange;
            firstPara := textRange.firstPara;
            lastPara := textRange.lastPara;
            numParas := SELF.textImage.text.paragraphs.size;

            {If the last paragraph is selected, treat this like a non-para selection, so that one
             empty para will be left at the end after the delete}
            IF textRange.lastIndex = numParas THEN
                SELF.isParaSelection := FALSE;

            s := SELF.textImage.text.paragraphs.ScannerFrom(textRange.firstIndex-1, scanForward);
            WHILE s.Scan(paragraph) DO
                IF paragraph = firstPara THEN
                    BEGIN
                    IF SELF.isParaSelection THEN
                        {If isParaSelection is TRUE then insert it in our save list and
                         delete it from the textImage's list}
                        BEGIN
                        IF saveIt THEN
                            paraList.InsLast(firstPara);
                        SELF.textImage.text.DelPara(firstPara, FALSE);
                        s.Delete(NOT saveIt);
                        END
                    ELSE
                        {If the beginning of the selection is part of a paragraph, then save
                        the characters in a new paragraph and delete them in the old}
                        BEGIN
                        selSize := firstPara.size - textRange.firstLP;
                        IF saveIt THEN
                            BEGIN
                            paragraph := SELF.textImage.NewEditPara(selSize, firstPara.format);
                            paragraph.ReplPara(0, 0, firstPara, textRange.firstLP, selSize);
                            paraList.InsLast(paragraph);
                            END;
                        firstPara.ReplPString(textRange.firstLP, selSize, NIL);
                        END;
                    IF firstPara = lastPara THEN
                        s.Done
                    END
                ELSE
                    BEGIN
                    s.Delete(FALSE);
                    IF paragraph = lastPara THEN
                        BEGIN
                        s.Done;
                        IF NOT SELF.isParaSelection THEN
                            {If the end of the selection is a part of a paragraph, then save the
                             selected characters in a new paragraph and append the rest to the
                             first paragraph.  Finally, delete and free the last paragraph }
                            BEGIN
                            selSize := textRange.lastLP;
                            IF saveIt THEN
                                BEGIN
                                paragraph := SELF.textImage.NewEditPara(selSize, lastPara.format);
                                paragraph.ReplPara(0, 0, lastPara, 0, selSize);
                                END;
                            firstPara.ReplPara(firstPara.size, 0, lastPara, selSize,
                                                                    lastPara.size-selSize);
                            SELF.textImage.text.DelPara(lastPara, TRUE);
                            END
                        ELSE
                            SELF.textImage.text.DelPara(paragraph, NOT saveIt);
                        END
                    ELSE
                        {Delete entire intermediate paragraphs from the textImage}
                        SELF.textImage.text.DelPara(paragraph, NOT saveIt);
                    IF saveIt THEN
                        paraList.InsLast(paragraph);
                    END;
            {$IFC fTrace}EP;{$ENDC}
        END; {DelText}

        PROCEDURE SetRange;
        BEGIN
            WITH SELF, textRange DO
                IF isParaSelection THEN
                    BEGIN
                    {$H-}
                    firstPara := TEditPara(textImage.text.paragraphs.At(firstIndex));
                    {$H+}
                    firstLP := 0;
                    END;

            WITH SELF.textRange DO
                BEGIN
                lastPara := firstPara;
                lastIndex := firstIndex;
                lastLP := firstLP;
                END;
        END;

    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        SELF.ChangeText(DelText, SetRange);
        Delete := text;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TMultiParaSelection.DeleteAndFree;
    VAR text:   TText;  {dummy var since Delete returns TText; will always be NIL}
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        text := SELF.Delete(FALSE);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TMultiParaSelection.DeleteButSave: TText;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        DeleteButSave := SELF.Delete(TRUE);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TMultiParaSelection.MouseMove(mouseLPt: LPoint);
        { assumes highlighting is ON }
    VAR endPara:	    TEditPara;
        currPara:	    TEditPara;
        currPImage:	    TParaImage;
        oldPImage:	    TParaImage;
        oldPara:	    TEditPara;
        paragraph:	    TEditPara;
        textrange:	    TTextRange;
        textImage:	    TTextImage;
        firstTxtImg:	    TTextImage;
        s:		    TListScanner;
        paraImage:	    TParaImage;
        newText:	    TText;
        currLP:		    INTEGER;
        oldLP:		    INTEGER;    { end (ie. not the anchor) of indication }
        mouseBeforeAnchor:  BOOLEAN;    { is mouse LPt before the anchor LPt }
        oldBeforeCurr:	    BOOLEAN;    { is old end LPt before curr LPt }
        beginIsAnchor:	    BOOLEAN;
        currIndex:	    LONGINT;
        oldIndex:	    LONGINT;
        startIndex:	    LONGINT;
        endIndex:	    LONGINT;
        startLP:	    INTEGER;
        endLP:		    INTEGER;
        first, last:	    INTEGER;
        selChanged:	    BOOLEAN;
        {$IFC fTextTrace}
        str1:		     STR255;
        str2:		     STR255;
        {$ENDC}

        PROCEDURE NextIndex(oldIndex: LONGINT; oldLP: INTEGER; VAR newIndex: LONGINT; VAR newLP: INTEGER);
        BEGIN
            IF oldIndex < SELF.textImage.text.paragraphs.size THEN
                BEGIN
                newIndex := oldIndex + 1;
                newLP := 0;
                END
            ELSE
                BEGIN
                newIndex := oldIndex;
                newLP := oldLP;
                END;
        END;

        PROCEDURE PrevIndex(oldIndex: LONGINT; oldLP: INTEGER; VAR newIndex: LONGINT; VAR newLP: INTEGER);
        BEGIN
            IF oldIndex > 1 THEN
                BEGIN
                newIndex := oldIndex - 1;
                newLP := TEditPara(SELF.textImage.text.paragraphs.At(newIndex)).size;
                END
            ELSE
                BEGIN
                newIndex := oldIndex;
                newLP := oldLP;
                END;
        END;

        PROCEDURE HiExtOnPads(highTransit: THighTransit;
                                    startIndex: LONGINT; startLP: INTEGER;
                                    endIndex: LONGINT; endLP: INTEGER);
        BEGIN
            SELF.textImage.text.HiliteParagraphs(highTransit, startIndex, startLP, endIndex, endLP,
                                                                            SELF.isParaSelection);
        END;

    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.currLPt := mouseLPt;

        {$IFC fTextTrace}
        IF fTextTrace THEN
            BEGIN
            LIntToHex(ORD(SELF.textRange.firstPara), @str1);
            LIntToHex(ORD(SELF.textRange.lastPara), @str2);
            Writeln;
            Writeln('*** MultiPara MouseMove: firstPara, lastPara = (', str1, ',', str2, ')');
            Writeln('*** About to call FindParaAndLp');
            END;
        {$ENDC}

        textRange := SELF.textRange;
        textImage := SELF.textImage.FindTextImage(mouseLPt, firstTxtImg);
        SELF.textImage := firstTxtImg;
        textImage.FindParaAndLp(mouseLPt, currPImage, currIndex, currLP);
        currPara := currPImage.paragraph;

        IF SELF.isParaSelection THEN
            BEGIN
            IF currIndex < SELF.anchorIndex THEN
                currLP := 0
            ELSE
                currLP := currPara.size;
            END
        ELSE IF SELF.isWordSelection THEN
            BEGIN
            currPara.FindWordBounds(currLP, first, last);
            IF currIndex < SELF.anchorIndex THEN
                currLP := first
            ELSE IF currIndex > SELF.anchorIndex THEN
                currLP := last
            ELSE IF first <= SELF.anchorBegin THEN
                currLP := first
            ELSE
                currLP := last;
            END;

        IF currIndex = SELF.anchorIndex THEN
            mouseBeforeAnchor := currLP < SELF.anchorBegin
        ELSE
            mouseBeforeAnchor := currIndex < SELF.anchorIndex;

        beginIsAnchor := (textRange.firstIndex = SELF.anchorIndex) AND (textRange.firstLP = SELF.anchorBegin);

        {After determining if the mouse is before or after the anchor position, set up variables for
         higlighting below and dehilite any text that was on other side of the anchor previous to this
         mouse move}
        IF mouseBeforeAnchor THEN
            BEGIN
            oldIndex := textRange.firstIndex;
            oldLP := textRange.firstLP;
            IF beginIsAnchor THEN
                {current Position is on other side of anchor, so must dehighlight}
                BEGIN
                IF SELF.isParaSelection THEN
                    IF SELF.anchorIndex = textRange.lastIndex THEN
                        startIndex := -1
                    ELSE
                        NextIndex(SELF.anchorIndex, SELF.anchorEnd, startIndex, startLP)
                ELSE
                    BEGIN
                    startIndex := SELF.anchorIndex;
                    startLP := SELF.anchorEnd;
                    END;
                IF startIndex > 0 THEN
                    HiExtOnPads(hOnToOff, startIndex, startLP, textRange.lastIndex, textRange.lastLP);
                END;
            WITH SELF, textRange DO
                BEGIN
                firstPara := currPara;
                firstIndex := currIndex;
                lastPara := anchorPara;
                lastIndex := anchorIndex;
                firstLP := currLP;
                lastLP := anchorEnd;
                END;
            END
        ELSE
            BEGIN
            oldIndex := textRange.lastIndex;
            oldLp := textRange.lastLP;
            IF NOT beginIsAnchor THEN
                BEGIN
                {current Position is on other side of anchor, so must dehighlight}
                IF SELF.isParaSelection THEN
                    PrevIndex(SELF.anchorIndex, SELF.anchorBegin, startIndex, startLP)
                ELSE
                    BEGIN
                    startIndex := SELF.anchorIndex;
                    startLP := SELF.anchorBegin;
                    END;
                HiExtOnPads(hOnToOff, textRange.firstIndex, textRange.firstLP, startIndex, startLP);
                END;
            WITH SELF, textRange DO
                BEGIN
                lastPara := currPara;
                lastIndex := currIndex;
                firstPara := anchorPara;
                firstIndex := anchorIndex;
                firstLP := anchorBegin;
                lastLP := currLP;
                END;
            END;

        IF mouseBeforeAnchor = beginIsAnchor THEN
            oldBeforeCurr := NOT mouseBeforeAnchor
        ELSE IF currIndex = oldIndex THEN
            oldBeforeCurr := oldLP < currLP
        ELSE
            oldBeforeCurr := oldIndex < currIndex;

        IF oldBeforeCurr THEN
            BEGIN
            startIndex := oldIndex;
            startLP := oldLP;
            endIndex := currIndex;
            endLP := currLP;
            END
        ELSE
            BEGIN
            startIndex := currIndex;
            startLP := currLP;
            endIndex := oldIndex;
            endLP := oldLP;
            END;

        selChanged := TRUE;
        IF SELF.isParaSelection THEN
            IF startIndex = endIndex THEN
                selChanged := FALSE
            ELSE IF mouseBeforeAnchor THEN
                PrevIndex(endIndex, endLP, endIndex, endLP)
            ELSE
                NextIndex(startIndex, startLP, startIndex, startLP)
        ELSE
            selChanged := (startIndex <> endIndex) OR (startLP <> endLP);

        IF selChanged THEN
            HiExtOnPads(hOffToOn, startIndex, startLP, endIndex, endLP);

        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TMultiParaSelection.MousePress(mouseLPt: LPoint);
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF clickState.fShift THEN
            SELF.MouseMove(mouseLPt)
        ELSE IF clickState.clickCount > 1 THEN
            BEGIN
            {For now do nothing if some jerk starts double/triple clicking while dragging}
            END
        ELSE
            SELF.textImage.MousePress(mouseLPt);
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TMultiParaSelection.MouseRelease;
    VAR insPt:	    TInsertionPoint;
        oneParaSel: TOneParaSelection;
        first,last: INTEGER;
        isPara:	    BOOLEAN;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF SELF.textRange.firstPara = SELF.textRange.lastPara THEN
            BEGIN
            isPara := SELF.isParaSelection;
            IF SELF.textRange.firstLP = SELF.textRange.lastLP THEN
                BEGIN
                insPt := SELF.BecomeInsertionPoint;
                insPt.isParaSelection := isPara;
                IF NOT isPara THEN
                    insPt.textImage.text.HiliteRange(hOffToOn, insPt.textRange, FALSE);
                END
            ELSE
                BEGIN
                first := SELF.anchorBegin;
                last := SELF.anchorEnd;
                WITH SELF, textRange DO
                    {$H-}
                    oneParaSel := TOneParaSelection(SELF.FreedAndReplacedBy(TOneParaSelection.CREATE(NIL,
                                            SELF.Heap, view, textImage, anchorLPoint,
                                            firstPara, firstIndex, firstLP, lastLP)));
                    {$H+}
                WITH oneParaSel DO
                    BEGIN
                    anchorBegin := first;
                    anchorEnd := last;
                    isParaSelection := isPara;
                    isWordSelection := NOT isPara AND (first <> last);
                    END;
                SELF.textImage.text.ChangeSelInOtherPanels(oneParaSel);
                END;
            END
        ELSE
            SELF.textImage.text.ChangeSelInOtherPanels(SELF);
        SUPERSELF.MouseRelease;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    FUNCTION TMultiParaSelection.SelSize: INTEGER;
    VAR size:	    INTEGER;
        s:	    TListScanner;
        paragraph:  TEditPara;
    BEGIN
        {$IFC fTrace}BP(9);{$ENDC}
        IF SELF.textRange.firstPara = SELF.textRange.lastPara THEN
            size := SELF.textRange.lastLP - SELF.textRange.firstLP
        ELSE
            BEGIN
            size := SELF.textRange.firstPara.size - SELF.textRange.firstLP;
            {skip first paragraph by not subtracting one from firstIndex}
            s := SELF.textImage.text.paragraphs.ScannerFrom(SELF.textRange.firstIndex, scanForward);
            WHILE s.Scan(paragraph) DO
                IF paragraph = SELF.textRange.lastPara THEN
                    BEGIN
                    size := size + SELF.textRange.lastLP;
                    s.Done;
                    END
                ELSE
                    size := size + paragraph.size;
            END;
        SelSize := size;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtCld}
    PROCEDURE TMultiParaSelection.StyleFromContext;
    VAR typeStyle:  TTypeStyle;
    BEGIN
        {$IFC fTrace}BP(8);{$ENDC}
        SELF.textRange.firstPara.StyleAt(SELF.textRange.firstLP, typeStyle);
        SELF.currTypeStyle := typeStyle;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$S SgTxtIni}
END; {Methods of TMultiParaSelection}


{$S SgTxtCld}

METHODS OF TClearTextCmd;

    FUNCTION TClearTextCmd.CREATE(object: TObject; heap: THeap; itsCmdNumber: TCmdNumber;
                                        itsImage: TImage; itsText: TText): TClearTextCmd;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        IF object = NIL THEN
            object := NewObject(heap, THISCLASS);
        SELF := TClearTextCmd(TCommand.CREATE(object, heap, itsCmdNumber, itsImage, TRUE, revealAll));
        WITH SELF DO
            BEGIN
            savedText := NIL;
            text := itsText;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;

    PROCEDURE TClearTextCmd.Free;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF SELF.savedText <> NIL THEN
            SELF.savedText.FreeSelf(FALSE);
        SUPERSELF.Free;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$IFC fTextTrace}
    PROCEDURE TClearTextCmd.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        SUPERSELF.Fields(Field);
        Field('savedText: TText');
        Field('text: TText');
        Field('');
    END;
{$ENDC}

    PROCEDURE TClearTextCmd.Commit;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        Free(SELF.savedText);
        SELF.savedText := NIL;
        {$IFC fTrace}EP;{$ENDC}
    END;


    PROCEDURE TClearTextCmd.Perform(cmdPhase: TCmdPhase);
    var textSel:        TTextSelection;
        insertionPt:    TInsertionPoint;
        text:	        TText;
        selection:      TSelection;
        panel:	        TPanel;
        {$IFC fTextTrace}
        junk:	        TObject;
        str1:	        STR255;
        str2:	        STR255;
        {$ENDC}


    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        panel := SELF.image.view.panel;
        text := SELF.text;
        CASE cmdPhase OF
            doPhase, redoPhase:
                BEGIN
                selection := panel.selection;
                WHILE selection.coSelection <> NIL DO
                    selection := selection.coSelection;

                {$IFC fTextTrace}
                IF fTextTrace THEN
                    BEGIN
                    LIntToHex(ORD(selection), @str1);
                    Writeln('*** Clear Cmd Perfrom; panel last coselection = ', str1);
                    junk := SELF.text.paragraphs.First; {So can set break point here}
                    END;
                {$ENDC}

                textSel := TTextSelection(selection.FreedAndReplacedBy(
                                    text.SelectAll(TTextSelection(selection).textImage)));
                text.ChangeSelInOtherPanels(textSel);
                text.HiliteRange(hOffToOn, textSel.textRange, FALSE);
                text := textSel.DeleteButSave;
                SELF.savedText := text;
                insertionPt := textSel.BecomeInsertionPoint;
                {$IFC fTextTrace}
                IF fTextTrace THEN
                    BEGIN
                    LIntToHex(ORD(insertionPt), @str1);
                    Writeln('*** Clear Cmd Perfrom; final insertionPt = ', str1);
                    junk := SELF.text.paragraphs.First; {So can set break point here}
                    END;
                {$ENDC}

                END;
            undoPhase:
                BEGIN
                selection := panel.selection;
                WHILE selection.coSelection <> NIL DO
                    selection := selection.coSelection;

                insertionPt := TInsertionPoint(selection);
                insertionPt.InsertText(SELF.savedText, FALSE, FALSE, FALSE);
                Free(SELF.savedText);
                SELF.savedtext := NIL;
                {$$ Need to hilte before, after?}
                SELF.text.ChangeSelInOtherPanels(insertionPt);
                END;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtIni}
END; {METHODS OF TClearTextCmd}
{$S SgTxtCld}


METHODS OF TStyleCmd;

    FUNCTION TStyleCmd.CREATE(object: TObject; heap: THeap; itsCmdNumber: TCmdNumber;
                                itsImage: TImage;
                                itsFirstIndex: LONGINT; itsLastIndex:  LONGINT;
                                itsLPFirst: INTEGER; itsLPLast: INTEGER;
                                itsSelection: TTextSelection): TStyleCmd;
    VAR sel:    TTextSelection;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        IF object = NIL THEN
            object := NewObject(heap, THISCLASS);
        SELF := TStyleCmd(TCommand.CREATE(object, heap, itsCmdNumber, itsImage, TRUE, revealSome));
        sel := TTextSelection(itsSelection.Clone(SELF.Heap));
        WITH SELF DO
            BEGIN
            textSelection := sel;
            text := sel.textImage.text;
            firstFiltParaIndex := itsFirstIndex;
            lastFiltParaIndex := itsLastIndex;
            filtFirstLP := itsLPFirst;
            filtLastLP := itsLPLast;
            currFilteredPara := NIL;
            filteredStyles := NIL;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;

    PROCEDURE TStyleCmd.Free;
    VAR sPar:	    TListScanner;
        paragraph:  TEditPara;
        paraImage:  TParaImage;

    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        sPar := SELF.text.paragraphs.ScannerFrom(SELF.firstFiltParaIndex - 1, scanForward);
        WHILE sPar.Scan(paragraph) DO
            BEGIN
            paragraph.beingFiltered := FALSE;
            IF sPar.position = SELF.lastFiltParaIndex THEN
                sPar.Done;
            END;
        SELF.textSelection.Free;
        Free(SELF.filteredStyles);
        SUPERSELF.Free;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$IFC fTextTrace}
    PROCEDURE TStyleCmd.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        SUPERSELF.Fields(Field);
        Field('text: TText');
        Field('textSelection: TTextSelection');
        Field('firstFiltParaIndex: LONGINT');
        Field('lastFiltParaIndex: LONGINT');
        Field('filtFirstLP: INTEGER');
        Field('filtLastLP: INTEGER');
        Field('currFilteredPara: TEditPara');
        Field('filteredStyles: TArray');
        Field('');
    END;
{$ENDC}


    PROCEDURE TStyleCmd.Commit;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        SELF.textSelection.ChangeStyle(SELF.cmdNumber);
        {$IFC fTrace}EP;{$ENDC}
    END;


    PROCEDURE TStyleCmd.FilterAndDo(actualObject: TObject;
                                        PROCEDURE DoToObject(filteredObject: TObject));
    VAR savedStyles:    TArray;
        paragraph:      TEditPara;
        typeStyle:      TTypeStyle;
        firstLP:        INTEGER;
        lastLP:	        INTEGER;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        paragraph := TParaImage(actualObject).paragraph;
        IF paragraph.beingFiltered THEN
            BEGIN
            IF paragraph = SELF.currFilteredPara THEN
                BEGIN
                savedStyles := paragraph.typeStyles;
                paragraph.typeStyles := SELF.filteredStyles;
                END
            ELSE
                BEGIN
                IF paragraph = TEditPara(SELF.text.paragraphs.At(SELF.firstFiltParaIndex)) THEN
                    firstLP := SELF.filtFirstLP
                ELSE
                    firstLP := 0;
                IF paragraph = TEditPara(SELF.text.paragraphs.At(SELF.lastFiltParaIndex)) THEN
                    lastLP := SELF.filtLastLP
                ELSE
                    lastLP := paragraph.size;
                Free(SELF.filteredStyles);
                savedStyles := TArray(paragraph.typeStyles.Clone(SELF.heap));
                SELF.textSelection.DoChangeStyle(SELF.cmdNumber, paragraph, firstLP, lastLP, typeStyle);
                SELF.currFilteredPara := paragraph;
                SELF.filteredStyles := paragraph.typeStyles;
                END;

            DoToObject(TParaImage(actualObject));

            paragraph.typeStyles := savedStyles;
            END
        ELSE
            DoToObject(TParaImage(actualObject));
        {$IFC fTrace}EP;{$ENDC}
    END;


    PROCEDURE TStyleCmd.Perform(cmdPhase: TCmdPhase);
    VAR textSelection:  TTextSelection;
        selection:      TSelection;
        sPar:	        TListScanner;
        paragraph:      TEditPara;

        {Need to filter paragraph before asking about its type styles}
        PROCEDURE FindFilteredStyle(obj: TObject);
            BEGIN
            textSelection.StyleFromContext;
            END;

    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        selection := SELF.image.view.panel.selection;
        WHILE selection.coSelection <> NIL DO
            selection := selection.coSelection;

        textSelection := TTextSelection(selection);
        IF cmdPhase = doPhase THEN
            BEGIN
            sPar := SELF.text.paragraphs.ScannerFrom(SELF.firstFiltParaIndex - 1, scanForward);
            WHILE sPar.Scan(paragraph) DO
                BEGIN
                paragraph.beingFiltered := TRUE;
                IF sPar.position = SELF.lastFiltParaIndex THEN
                    sPar.Done;
                END;
            END;
        textSelection.MarkChanged;
        textSelection.textImage.text.RecomputeImages;
        SELF.FilterAndDo(TParaImage(TEditPara(SELF.text.paragraphs.At(
                            SELF.firstFiltParaIndex)).images.First), FindFilteredStyle);
        (*textSelection.Invalidate;*)
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtIni}
END; {METHODS OF TStyleCmd}
{$S SgTxtCld}


METHODS OF TTextCutCopy;

    FUNCTION TTextCutCopy.CREATE(object: TObject; heap: THeap; itsCmdNumber: TCmdNumber;
                                        itsImage: TImage; isCutCmd: BOOLEAN; itsText: TText): TTextCutCopy;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        IF object = NIL THEN
            object := NewObject(heap, THISCLASS);
        SELF := TTextCutCopy(TCutCopyCommand.CREATE(object, heap, itsCmdNumber, itsImage, isCutCmd));
        WITH SELF DO
            BEGIN
            text := itsText;
            IF itsCmdNumber = uCopy THEN
                BEGIN
                unHiliteBefore[doPhase] := FALSE;
                hiliteAfter[doPhase] := FALSE;
                END;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


{$IFC fTextTrace}
    PROCEDURE TTextCutCopy.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        TCutCopyCommand.Fields(Field);
        Field('text: TText');
        Field('');
    END;
{$ENDC}


    PROCEDURE TTextCutCopy.DoCutCopy(clipSelection: TSelection; deleteOriginal: BOOLEAN;
                                         cmdPhase: TCmdPhase);
    VAR textSel:        TTextSelection;
        insertionPt:    TInsertionPoint;
        multiParaSel:   TMultiParaSelection;
        heap:	        THeap;
        firstPara:      TEditPara;
        firstLP:        INTEGER;
        panel:	        TPanel;
        selection:      TSelection;
        saveTextSel:    TTextSelection;
        firstIndex:     LONGINT;

    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        heap := SELF.Heap;
        panel := SELF.image.view.panel;
        CASE cmdPhase OF
            doPhase, redoPhase:
                BEGIN
                selection := panel.selection;

                {we know that the last coSelection must be the textSelection since textSelections
                 do not have coSelections}
                WHILE selection.coSelection <> NIL DO
                    selection := selection.coSelection;

                textSel := TTextSelection(selection);
                IF (cmdPhase = redoPhase) AND deleteOriginal THEN
                    SELF.text.HiliteRange(hOffToOn, textSel.textRange, textSel.isParaSelection);

                textSel.CutCopy(clipSelection, deleteOriginal);
                END;
            undoPhase:
                BEGIN
                IF deleteOriginal THEN
                    BEGIN
                    selection := panel.selection;
                    WHILE selection.coSelection <> NIL DO
                        selection := selection.coSelection;
                    insertionPt := TInsertionPoint(selection);
                    firstPara := insertionPt.textRange.firstPara;
                    firstLP := insertionPt.textRange.firstLP;
                    firstIndex := insertionPt.textRange.firstIndex;

                    {get the cut text from the clipboard and insert it back into the text}
                    multiParaSel := TMultiParaSelection(clipSelection);
                    insertionPt.InsertText(multiParaSel.textImage.text, multiParaSel.isParaSelection,
                                                                    multiParaSel.isWordSelection, FALSE);

                    IF multiParaSel.isParaSelection THEN
                        BEGIN
                        WITH insertionPt DO
                            IF textRange.firstIndex > 1 THEN
                              BEGIN
                              textRange.firstIndex := textRange.firstIndex - 1;
                              {$H-}
                              textRange.firstPara := TEditPara(SELF.text.paragraphs.At(textRange.firstIndex));
                              textRange.firstLP := textRange.firstPara.size;
                              {$H+}
                              END;
                        END
                    ELSE IF multiParaSel.isWordSelection THEN
                        {don't want to select extra blank generated by insert}
                        WITH insertionPt DO
                            {$H-}
                            IF firstPara.At(firstLP + 1) = ' ' THEN
                                firstLP := firstLP + 1
                            ELSE IF textRange.firstPara.At(textRange.firstLP) = ' ' THEN
                                textRange.firstLP := textRange.firstLP - 1;
                            {$H+}

                    {build the original selection}
                    textSel := TTextSelection(selection.FreedAndReplacedBy(
                                    insertionPt.textImage.NewTextSelection(
                                        firstPara, firstIndex, firstLP, insertionPt.textRange.firstPara,
                                        insertionPt.textRange.firstIndex, insertionPt.textRange.firstLP)));
                    textSel.isParaSelection := multiParaSel.isParaSelection;

                    SELF.text.ChangeSelInOtherPanels(textSel);
                    END;
                END;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtIni}
END; {METHODS OF TTextCutCopy}
{$S SgTxtCld}


METHODS OF TTextPaste;

    FUNCTION TTextPaste.CREATE(object: TObject; heap: THeap; itsImage: TImage;
                                        itsText: TText): TTextPaste;
    VAR range:  TTextRange;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        IF object = NIL THEN
            object := NewObject(heap, THISCLASS);
        SELF := TTextPaste(TPasteCommand.CREATE(object, heap, uPaste, itsImage));
        {need noSelection since it gets FreedAndReplacedBy'ed}
        range := TTextRange.CREATE(NIL, heap, NIL, 0, 0, NIL, 0, 0); {Perform initializes}
        WITH SELF DO
            BEGIN
            pasteRange := range;
            text := itsText;
            savedText := NIL;
            origIsPara := FALSE;
            origIsWord := FALSE;
            clipIsPara := FALSE;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;

    PROCEDURE TTextPaste.Free;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        IF SELF.savedText <> NIL THEN
            SELF.savedText.FreeSelf(FALSE);
        Free(SELF.pasteRange);
        SUPERSELF.Free;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$IFC fTextTrace}
    PROCEDURE TTextPaste.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        SUPERSELF.Fields(Field);
        Field('savedText: TText');
        Field('pasteRange: TTextRange');
        Field('text: TText');
        Field('origIsPara: BOOLEAN');
        Field('origIsWord: BOOLEAN');
        Field('clipIsPara: BOOLEAN');
        Field('');
    END;
{$ENDC}

    PROCEDURE TTextPaste.Commit;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        Free(SELF.savedText);
        SELF.savedText := NIL;
        {$IFC fTrace}EP;{$ENDC}
    END;

    PROCEDURE TTextPaste.DoPaste(clipSelection: TSelection; pic: PicHandle; cmdPhase: TCmdPhase);
    VAR heap:	        THeap;
        textSel:        TTextSelection;
        saveTextSel:    TTextSelection;
        insertionPt:    TInsertionPoint;
        insPtBefore:    TInsertionPoint;
        insPtAfter:     TInsertionPoint;
        text:	        TText;
        firstPara:      TEditPara;  {bad choice of var names; change later (screws up WITH's)}
        firstLP:        INTEGER;
        firstIndex:     LONGINT;
        panel:	        TPanel;
        selection:      TSelection;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        heap := SELF.Heap;
        panel := SELF.image.view.panel;
        CASE cmdPhase OF
            doPhase, redoPhase:
                BEGIN
                IF InClass(clipSelection, TTextSelection) OR (clipBoard.hasUniversalText) THEN
                    BEGIN
                    selection := panel.selection;
                    {we know that the last coSelection must be the textSelection since textSelections
                     do not have coSelections}
                    WHILE selection.coSelection <> NIL DO
                        selection := selection.coSelection;
                    textSel := TTextSelection(selection);
                    SELF.origIsPara := textSel.isParaSelection;
                    SELF.origIsWord := textSel.isWordSelection;
                    IF InClass(clipSelection, TTextSelection) THEN
                        SELF.clipIsPara := TTextSelection(clipSelection).isParaSelection;

                    {delete the selected text, leaving an insertion point}
                    text := textSel.DeleteButSave;
                    SELF.savedText := text;
                    insertionPt := textSel.BecomeInsertionPoint;
                    WITH SELF, insertionPt DO
                        BEGIN
                        pasteRange.firstPara := textRange.firstPara;
                        pasteRange.firstIndex := textRange.firstIndex;
                        pasteRange.firstLP := textRange.firstLP;
                        END;

                    insertionPt.FinishPaste(clipSelection, pic);

                    WITH SELF, insertionPt DO
                        IF clipIsPara THEN
                            BEGIN
                            {$H-}
                            pasteRange.lastIndex := Max(1, textRange.firstIndex - 1);
                            pasteRange.lastPara := TEditPara(SELF.text.paragraphs.At(pasteRange.lastIndex));
                            pasteRange.lastLP := pasteRange.lastPara.size;
                            {$H+}
                            END
                        ELSE
                            BEGIN
                            pasteRange.lastPara := textRange.firstPara;
                            pasteRange.lastIndex := textRange.firstIndex;
                            pasteRange.lastLP := textRange.firstLP;
                            END;

                    SELF.text.ChangeSelInOtherPanels(insertionPt);
                    END
                ELSE
                    BEGIN
                    panel.selection.CantDoIt;
                    SELF.undoable := FALSE;
                    END;
                END;
            undoPhase:
                BEGIN
                WITH SELF.pasteRange DO
                    {$H-}
                    textSel := TTextImage(SELF.image).NewTextSelection(firstPara, firstIndex, firstLP,
                                                                       lastPara, lastIndex, lastLP);
                    {$H+}
                textSel.isParaSelection := SELF.clipIsPara;

                {user feedback: highlight pasted text}
                SELF.text.HiliteRange(hOffToOn, SELF.pasteRange, textSel.isParaSelection);

                {get rid of pasted text; can get it from clipboard for redo}
                textSel.DeleteAndFree;

                insertionPt := textSel.BecomeInsertionPoint;

                firstPara := insertionPt.textRange.firstPara;
                firstLP := insertionPt.textRange.firstLP;
                firstIndex := insertionPt.textRange.firstIndex;

                {put back any text that was pasted over}
                insertionPt.InsertText(SELF.savedText, SELF.origIsPara, SELF.origIsWord, FALSE);

                WITH insertionPt DO
                    IF SELF.origIsPara THEN
                        BEGIN
                        {$H-}
                        textRange.firstIndex := Max(1, textRange.firstIndex - 1);
                        textRange.firstPara := TEditPara(SELF.text.paragraphs.At(textRange.firstIndex));
                        textRange.firstLP := textRange.firstPara.size;
                        {$H+}
                        END;

                Free(SELF.savedText);
                SELF.savedText := NIL;

                selection := panel.selection;
                WHILE selection.coSelection <> NIL DO
                    selection := selection.coSelection;

                {build original selection (ie before the paste)}
                textSel := TTextSelection(selection.FreedAndReplacedBy(
                                    insertionPt.textImage.NewTextSelection(
                                        firstPara, firstIndex, firstLP, insertionPt.textRange.firstPara,
                                        insertionPt.textRange.firstIndex, insertionPt.textRange.firstLP)));

                textSel.isParaSelection := SELF.origIsPara;
                textSel.isWordSelection := SELF.origIsWord;
                SELF.text.ChangeSelInOtherPanels(textSel);
                insertionPt.Free;
                END;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtIni}
END; {METHODS OF TTextPaste}
{$S SgTxtHot}

METHODS OF TTypingCmd;

    FUNCTION TTypingCmd.CREATE(object: TObject; heap: THeap; itsImage: TImage;
                                    itsText: TText): TTypingCmd;
    VAR range:  TTextRange;
    BEGIN
        {$IFC fTrace}BP(11);{$ENDC}
        IF object = NIL THEN
            object := NewObject(heap, THISCLASS);
        SELF := TTypingCmd(TCommand.CREATE(object, heap, uTyping, itsImage, TRUE, revealAll));
        range := TTextRange.CREATE(NIL, heap, NIL, 0, 0, NIL, 0, 0); {Perform initializes}
        WITH SELF DO
            BEGIN
            newCharCount := 0;
            newParaCount := 0;
            text := itsText;
            savedText := NIL;
            typingRange := range;
            otherInsPts := NIL;
            hiliteAfter[doPhase] := FALSE;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;


    PROCEDURE TTypingCmd.Free;
    VAR selection: TSelection;
    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        Free(SELF.savedText);
        selection := SELF.image.view.panel.selection;
        WHILE selection.coSelection <> NIL DO
            selection := selection.coSelection;

        IF InClass(selection, TInsertionPoint) THEN
            BEGIN
            TInsertionPoint(selection).typingCmd := NIL;
            TInsertionPoint(selection).amTyping := FALSE;
            END;

        SELF.typingRange.Free;
        IF SELF.otherInsPts <> NIL THEN
            SELF.otherInsPts.FreeObject;
        SUPERSELF.Free;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtCld}
{$IFC fTextTrace}
    PROCEDURE TTypingCmd.Fields(PROCEDURE Field(nameAndType: S255));
    BEGIN
        SUPERSELF.Fields(Field);
        Field('');
        Field('savedText: TText');
        Field('text: TText');
        Field('newCharCount: INTEGER');
        Field('newParaCount: INTEGER');
        Field('typingRange: TTextRange');
        Field('otherInsPts: TList');
        Field('');
    END;
{$ENDC}

{$S SgTxtHot}
    PROCEDURE TTypingCmd.Perform(cmdPhase: TCmdPhase);
    VAR text:	        TText;
        insertionPt:    TInsertionPoint;
        selection:      TSelection;
        heap:	        THeap;
        firstPara:      TEditPara;
        firstLP:        INTEGER;
        textSel:        TTextSelection;
        panel:	        TPanel;
        firstIndex:     LONGINT;
        aList:	        TList;
        typeStyle:      TTypeStyle;

        PROCEDURE InstallInsPts(obj: TObject);
        VAR selection: TSelection;
        BEGIN
            selection := TTextImage(obj).view.panel.selection;
            WHILE selection.coSelection <> NIL DO
                selection := selection.coSelection;
            IF selection <> insertionPt THEN
                aList.InsLast(selection);
        END;

    BEGIN
        {$IFC fTrace}BP(10);{$ENDC}
        heap := SELF.Heap;
        panel := SELF.image.view.panel;
        CASE cmdPhase OF
            doPhase, redoPhase:
                BEGIN
                selection := panel.selection;
                WHILE selection.coSelection <> NIL DO
                    selection := selection.coSelection;

                textSel := TTextSelection(selection);
                typeStyle := textSel.currTypeStyle;

                {We don't want to delete the entire paragraph if we're typing over it,
                 so set isParaSelection to FALSE}
                textSel.isParaSelection := FALSE;

(***** Changed following line 4/27/84 13:07 LSR
            BUG: redo of backspace (?) leaves garbage
                deferUpdate := TRUE;
*****)
                deferUpdate := (cmdPhase = doPhase) OR (SELF.savedText <> NIL);

                text := textSel.DeleteButSave;
                insertionPt := textSel.BecomeInsertionPoint;
                WITH SELF.typingRange DO
                    BEGIN
                    firstPara := insertionPt.textRange.firstPara;
                    firstIndex := insertionPt.textRange.firstIndex;
                    firstLP := insertionPt.textRange.firstLP;
                    lastPara := firstPara;
                    lastIndex := firstIndex;
                    lastLP := firstLP;
                    END;
                IF cmdPhase = doPhase THEN
                    BEGIN
                    WITH insertionPoint DO
                        BEGIN
                        amTyping := TRUE;
                        typingCmd := SELF;
                        IF TFakeTStyle(currTypeStyle) <> TFakeTStyle(typeStyle) THEN
                            BEGIN
                            styleCmdNumber := -1; {so correct typeStyle will be used}
                            currTypeStyle := typeStyle;
                            END;
                        END;
                    {If there is more than one panel displaying this text, then store the insertion points
                     of the other panels in a list for quick access while typing}
                    IF SELF.text.txtImgList.size > 1 THEN
                        BEGIN
                        aList := TList.CREATE(NIL, heap, 0);
                        SELF.text.txtImgList.Each(InstallInsPt);
                        SELF.otherInsPts := aList;
                        END;
                    insertionPt.textRange.firstPara.StartEdit(insertionPt.textRange.firstPara.GrowSize);
                    END
                ELSE
                    BEGIN
                    firstPara := insertionPt.textRange.firstPara;
                    firstLP := insertionPt.textRange.firstLP;
                    firstIndex := insertionPt.textRange.firstIndex;

                    {put back the typed text}
                    deferUpdate := FALSE;
                    insertionPt.InsertText(SELF.savedText, FALSE, FALSE, FALSE);

                    Free(SELF.savedText);

                    WITH SELF.typingRange DO
                        BEGIN
                        lastPara := insertionPt.textRange.firstPara;
                        lastIndex := insertionPt.textRange.firstIndex;
                        lastLP := insertionPt.textRange.firstLP;
                        END;
                    {build typed selection}
                    textSel := TTextSelection(insertionPt.FreedAndReplacedBy(
                                        insertionPt.textImage.NewTextSelection(
                                           firstPara, firstIndex, firstLP, insertionPt.textRange.firstPara,
                                           insertionPt.textRange.firstIndex, insertionPt.textRange.firstLP)));

                    SELF.text.ChangeSelInOtherPanels(textSel);
                    END;

                {We always need a valid savedText object, even if no characters were initially typed
                 over, in case previous characters get backspaced over.	 See code in KeyBack }
                IF text = NIL THEN
                    BEGIN
                    text := TText.CREATE(NIL, heap, NIL);
                    text.paragraphs.InsLast(
                                TTextImage(SELF.image).NewEditPara(0, SELF.typingRange.firstPara.format));
                    END;
                SELF.savedText := text;
                END;
            undoPhase:
                BEGIN
                WITH SELF.typingRange DO
                    {$H-}
                    textSel := TTextImage(SELF.image).NewTextSelection(firstPara, firstIndex, firstLP,
                                                                       lastPara, lastIndex, lastLP);
                    {$H+}

                {user feedback: highlight typed text}
                SELF.text.HiliteRange(hOffToOn, SELF.typingRange, textSel.isParaSelection);

                {delete but save typed text}
                text := textSel.DeleteButSave;

                insertionPt := textSel.BecomeInsertionPoint;

                firstPara := insertionPt.textRange.firstPara;
                firstLP := insertionPt.textRange.firstLP;
                firstIndex := insertionPt.textRange.firstIndex;

                {put back any text that was typed over}
                insertionPt.InsertText(SELF.savedText, FALSE, FALSE, FALSE);

                Free(SELF.savedText);
                SELF.savedText := text;

                selection := panel.selection;
                WHILE selection.coSelection <> NIL DO
                    selection := selection.coSelection;

                {build original selection (before typing)}
                textSel := TTextSelection(selection.FreedAndReplacedBy(
                                    insertionPt.textImage.NewTextSelection(
                                        firstPara, firstIndex, firstLP, insertionPt.textRange.firstPara,
                                        insertionPt.textRange.firstIndex, insertionPt.textRange.firstLP)));

                SELF.text.ChangeSelInOtherPanels(textSel);
                insertionPt.Free;
                END;
            END;
        {$IFC fTrace}EP;{$ENDC}
    END;

{$S SgTxtIni}
END; {METHODS OF TTypingCmd}
{$S SgTxtCld}


